Shader programming

Shader programming is probably one of the most difficult and low-level development tasks you can do as a graphics programmer. It requires an excellent knowledge of 3D math and the graphics rendering process. Also, writing good shaders is a skill that can take years to master. So why are we covering this in a book that covers fundamentals? Simply put, coding a good shader may be difficult, but it is also extremely rewarding, and it's a skillset that is essential to any serious 3D programmer.

We will be using shaders throughout the rest of this book for many things. If, at this point, you are starting to feel overwhelmed, then take a break and study some 3D math or jump ahead a chapter. Sometimes, you just need time for things to sink in before you get that eureka moment.

A shader program runs directly on the graphic processing unit (GPU) of the device or computer. If the device doesn't have a GPU, then the program is executed on the CPU, which is a much slower process. After all, the GPU has been optimized to run shader code and do it extremely well. In fact, virtually all 3D rendering done on the GPU runs the shader code. When we use Unity, a much higher-level game engine, we will still write our own shaders because of the power and flexibility it gives us.

So, what does a shader program look like? The following is an example of a shader written in the OpenGL Shading Language (GLSL):

uniform mat4 u_ModelViewProjection;
uniform vec4 u_Color;
uniform float u_PointSize;

attribute vec4 a_Position;

varying vec4 v_Color;

void main() {
v_Color = u_Color;
gl_Position = u_ModelViewProjection * vec4(a_Position.xyz, 1.0);
gl_PointSize = u_PointSize;
}

This is the shader program we use for rendering our point cloud points or vertices. Specifically, this shader is responsible for rendering a single vertex for each call to main, and it's called a vertex shader. Later in the rendering process, after the 3D scene is flattened to a 2D image with the vertex shaders, we have the opportunity to run a fragment or pixel shader. A fragment shader is run for every pixel/fragment that needs to be rendered.

Shader programs come in a few variations, but since they all derive from a C language and share so many similar functions, switching from one language to another isn't as difficult as you think. We will, in fact, learn some basics of the GLSL and the form used in Unity called High Level Shading Language (HLSL), which has its roots in DirectX.

If you look in the main function, you will see we are setting three variables: v_Color, gl_Position, and gl_PointSize. Those variables are global and just determine the color, size, and position of the vertex. The first line sets the color to an input variable—u_Color. Then, the position is calculated by multiplying the u_ModelViewProjection matrix with a new vector representing the position. That operation converts our vertex from world space to screen space. Finally, we set the point size with another input—u_PointSize.

What we want to do is modify that shader program so that it colorizes the points based on the distance from the user. Before we do that, though, let's take a look at how the shader gets those inputs. Open up Android Studio to PointCloudRenderer.java and follow along:

  1. Scroll down to bottom of the createOnGUIThread method and look for the following lines:
positionAttribute = GLES20.glGetAttribLocation(programName, "a_Position");
colorUniform = GLES20.glGetUniformLocation(programName, "u_Color");
modelViewProjectionUniform = GLES20.glGetUniformLocation(programName, "u_ModelViewProjection");
pointSizeUniform = GLES20.glGetUniformLocation(programName, "u_PointSize");
  1. Those lines of code set up our shader input positions. What we are doing here is determining the indexes we need for injecting data into the array buffer we pass to the shader. We need to add another input, so add the following line at the end of the preceding code snippet:
furthestPoint = GLES20.glGetUniformLocation(programName, "u_FurthestPoint");
  1. This line adds another input variable called u_FurthestPoint. We need to calculate the furthest point from the user (camera) in order to colorize the points on a gradient. Before we do that, go back to the top of the file and declare the following new variables under the line identified:
private int numPoints = 0;  //after this line
private int furthestPoint;
private float furthestPointLength;
  1. Remember that furthestPoint is an index to the variable and furthestPointLength will be used to hold the distance to the furthest point.
  2. Scroll down to the update method and enter the following code after the identified line:
numPoints = lastPointCloud.getPoints().remaining() / FLOATS_PER_POINT;  //after me

furthestPointLength = 1;
if(numPoints > 0) {
for(int i=0; i<numPoints*FLOATS_PER_POINT;i= i+FLOATS_PER_POINT) {
float x = lastPointCloud.getPoints().get(i);
float y = lastPointCloud.getPoints().get(i+1);
float z = lastPointCloud.getPoints().get(i+2);
double len = Math.sqrt(x*x+y*y+z*z);
furthestPointLength = Math.max(furthestPointLength, (float)len);
}
}
}
  1. This code first sets our minimum distance (1) to mFurthestPointLength. Then, we check whether there are any observed points. If there are, we loop through the points in the point cloud. In the loop, we use the get method to index into the point buffer and extract the x, y, and z of the points. This allows us to measure the length of the vector with x, y, and z of the point. You make recognize the equation as the Pythagorean theorem, but in 3 dimensions rather than the 2 you may be used to. We then check whether this new length (distance) is greater than the current furthest length with Math.max. Keep in mind that this code is run in the update method and thus executed every rendered frame.
We calculate the distance between two points in 3D space using the following formulae:



Since our camera (user) is the origin, we can assume that one of our points is (0,0,0), which is equal to this:


This becomes the following:

  1. Scroll down to the draw method and add the following code beneath the identified line:
GLES20.glUniform1f(mPointSizeUniform, 25.0f);  //after me

GLES20.glUniform1f(furthestPoint, furthestPointLength);
  1. This call sets the furthestPointLength that we calculated in the update method to the shader program.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.175.182