Cameron Honis
Cameron Honis

Reputation: 25

GLSL get pixel-specific distance from camera?

I am trying to get the distance of each pixel drawn to the canvas in the vertex shader. The vertex shader code is as follows:

attribute vec4 modelVertexPosition;

// cameraMatrix is strictly used as a ref to the camera's position in this case
// and does not contribute to the screen projection position calculation
uniform mat4 cameraMatrix;
uniform mat4 projectionMatrix;
uniform mat4 modelMatrix;

varying float cameraDistance;

void main(void) {
  // calc world position of vertex
  vec4 vertexPosition = modelMatrix * modelVertexPosition;
  // calc frustum position of vertex
  vec4 projVertexPosition = projectionMatrix * vertexPosition;
  gl_Position = projVertexPosition;
  // set cameraDistance varying for fragment shader
  cameraDistance = distance(cameraMatrix[3].xyz, (modelMatrix * scaledModelVertexPosition).xyz);
}

This works fine for all points strictly on the vertices of the triangles being drawn, but it's clear glsl is interpolating the distances of points inside the triangles linearly relative to their vertices. It's usually not a big issue, but it's especially noticeable when the camera is closer to the middle of the triangle than the vertices.

To demonstrate this issue, I set the fragment shader to set the brightness of the shape as a function of the distance of that point from the camera, the darker the closer. I set a situation that exposes the flaws in the method of implementation show below: enter image description here

So this begs the question, is there a way to get the position of each pixels distance from the camera?

Upvotes: 0

Views: 1777

Answers (2)

Adam Gawne-Cain
Adam Gawne-Cain

Reputation: 1690

The distance of a fragment from the camera is related to its depth in the depth buffer. You can calculate depth in the fragment shader as follows:

float depth = gl_Position.z / gl_Position.w;

The depth of visible fragments varies from 0 to 1, with 0 being on the near clip plane and 1 being on the far clip plane. You can calculate the distance from the camera with this formula:

depth = (1/camera_distance - 1/near)/(1/far - 1/near)

Note that depth and camera distance are not linearly related. Instead, depth is linearly related to 1/camera_distance. OpenGL etc define "depth" using the reciprocal of camera distance so hardware can do linear interpolation of "depth" across triangles in screen space to quickly determine which triangle is visible at each pixel in perspective views.

Upvotes: 2

Cameron Honis
Cameron Honis

Reputation: 25

Disclaimer: I'm still very new to the OpenGL framework, and actually I'm only experimenting with the watered down version which is WebGL, so take my solution with a grain of salt. Also if someone has a better method, I'm all ears.

Anyways, I found that only the fragment shader is capable of doing individual pixel calculations. On the contrary, the vertex shader makes assumptions of all the pixels between the vertices by calculating all the outputs of the shader for each vertex, then using interpolation to estimate the outputs for all the pixels between the vertices.

With this in mind, simply moving the code that calculates the distance from the camera to the fragment shader, this allows calculations to be done on a per pixel basis, which is the effect I was going for in this case. However I hypothesis this method is much less efficient, so this approach should only be used when absolutely necessary (in my estimation).

Again if anyone has more insight on this topic, please chime in. Criticism is more than welcomed.

Upvotes: 0

Related Questions