Triangle - Shade

Hello to everyone,

I’m fighting with OpenGLES, with shaders and matrix. I need to display triangles and shade them according to a directional light.

My code is the following.

public void OnDrawFrame (Javax.Microedition.Khronos.Opengles.IGL10 gl)
{
	gl.GlClear((int)GL10.GlColorBufferBit | GL10.GlDepthBufferBit);
 
	// Draw the heightmap.
	// Translate the heightmap into the screen.
	Matrix.SetIdentityM(mModelMatrix, 0);
	Matrix.TranslateM(mModelMatrix, 0, 0.0f, 0.0f, iZoomLevel);
 
	// scaling
	Matrix.SetIdentityM(mScaleMatrix, 0);
	Matrix.ScaleM(mScaleMatrix, 0, scaleX, scaleY, scaleZ);
 
	// Set a matrix that contains the current rotation.
	Matrix.SetIdentityM(mCurrentRotation, 0);
	Matrix.RotateM(mCurrentRotation, 0, mAngleX, 0.0f, 1.0f, 0.0f);
	Matrix.RotateM(mCurrentRotation, 0, mAngleY, 1.0f, 0.0f, 0.0f);
	mAngleX = 0.0f;
	mAngleY = 0.0f;
 
	// Multiply the current rotation by the accumulated rotation, and then
	// set the accumulated rotation to the result.
	Matrix.MultiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
	System.Array.Copy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);
 
	Matrix.MultiplyMM(mTemporaryMatrix, 0, mScaleMatrix, 0, mAccumulatedRotation, 0);
	System.Array.Copy(mTemporaryMatrix, 0, mScaleMatrix, 0, 16);
 
	// Rotate the cube taking the overall rotation into account.
	Matrix.MultiplyMM(mTemporaryMatrix, 0, mModelMatrix, 0, mScaleMatrix, 0); //mAccumulatedRotation, 0);
	System.Array.Copy(mTemporaryMatrix, 0, mModelMatrix, 0, 16);
 
	// This multiplies the view matrix by the model matrix, and stores
	// the result in the MVP matrix
	// (which currently contains model * view).
	Matrix.MultiplyMM(mMVPMatrix, 0, mVMatrix, 0, mModelMatrix, 0);
 
	Matrix.MultiplyMV(lightDirInEyeSpace, 0, mTemporaryMatrix, 0, lightDirInModelSpace, 0);
 
	// This multiplies the modelview matrix by the projection matrix,
	// and stores the result in the MVP matrix
	// (which now contains model * view * projection).
	Matrix.MultiplyMM(mTemporaryMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
	System.Array.Copy(mTemporaryMatrix, 0, mMVPMatrix, 0, 16);
 
	mTriangle1.Draw (mMVPMatrix, mVMatrix, lightDirInEyeSpace);
	mTriangle2.Draw (mMVPMatrix, mVMatrix, lightDirInEyeSpace);
}
 
public void OnSurfaceCreated (Javax.Microedition.Khronos.Opengles.IGL10 gl, Javax.Microedition.Khronos.Egl.EGLConfig config)
{
	// Set the background clear color to black.
	GLES20.GlClearColor(1.0f, 1.0f, 1.0f, 0.0f);
 
	// Enable depth testing
	GLES20.GlEnable(GLES20.GlDepthTest);
 
	// Position the eye in front of the origin.
	float eyeX = 0.0f;
	float eyeY = 0.0f;
	float eyeZ = -0.5f;
 
	// We are looking toward the distance
	float lookX = 0.0f;
	float lookY = 0.0f;
	float lookZ = -5.0f;
 
	// Set our up vector. This is where our head would be pointing were we
	// holding the camera.
	float upX = 0.0f;
	float upY = 1.0f;
	float upZ = 0.0f;
 
	// Set the view matrix. This matrix can be said to represent the camera
	// position.
	// NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination
	// of a model and view matrix. In OpenGL 2, we can keep track of these
	// matrices separately if we choose.
	Matrix.SetLookAtM(mVMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
 
	// Initialize the accumulated rotation matrix
	Matrix.SetIdentityM(mAccumulatedRotation, 0);
 
	mTriangle1 = new Triangle ();
	mTriangle1.SetVerts(0f,0f,0, 0.5f,0.0f,0, 0.0f,0.5f,0);
	mTriangle2 = new Triangle ();
	mTriangle2.SetVerts(0f,0f,0, 0.0f, 0.5f,0.0f, 0f,0.0f,0.5f);
}

My triangle class:

class Triangle
{
	private FloatBuffer vertexBuffer;
	private int mProgram;
	private int mPositionHandle;
	private int mColorHandle;
	private int mMVPMatrixHandle;
	private int mMVMatrixHandle;
	private int mLightDirHandle;
	private int mNormalHandle;
 
	// number of coordinates per vertex in this array
	static int COORDS_PER_VERTEX = 3;
	float[] triangleCoords = new float [] { // in counterclockwise order:
		-0.5f,  -0.5f, 0.0f,   // top
		0.5f, -0.5f, 0.0f,   // bottom left
		0.0f, 0.5f, 0.0f    // bottom right
	};
	private int vertexCount = 3; //triangleCoords.Length / COORDS_PER_VERTEX;
	private int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
 
	// Set color with red, green, blue and alpha (opacity) values
	float[] color = new float[] { 
		0.63671875f, 
		0.76953125f, 
		0.22265625f, 
		1.0f };
 
	public Triangle ()
	{
		// initialize vertex byte buffer for shape coordinates
		// (number of coordinate values * 4 bytes per float)
		ByteBuffer bb = ByteBuffer.AllocateDirect (	triangleCoords.Length * 4);
		// use the device hardware's native byte order
		bb.Order (ByteOrder.NativeOrder ());
 
		// create a floating point buffer from the ByteBuffer
		vertexBuffer = bb.AsFloatBuffer ();
		// add the coordinates to the FloatBuffer
		vertexBuffer.Put (triangleCoords);
		// set the buffer to read the first coordinate
		vertexBuffer.Position (0);
 
		// prepare shaders and OpenGL program
		int vertexShader = MyGLRenderer.LoadShader (GLES20.GlVertexShader,
			vertexShaderCode);
		int fragmentShader = MyGLRenderer.LoadShader (GLES20.GlFragmentShader,
			fragmentShaderCode);
 
		mProgram = GLES20.GlCreateProgram ();             // create empty OpenGL Program
		GLES20.GlAttachShader (mProgram, vertexShader);   // add the vertex shader to program
		GLES20.GlAttachShader (mProgram, fragmentShader); // add the fragment shader to program
		GLES20.GlLinkProgram (mProgram);                  // create OpenGL program executables
	}
 
	public void SetVerts(float v0, float v1, float v2, float v3, float v4, float v5,
		float v6, float v7, float v8)
	{
		triangleCoords[0] = v0;
		triangleCoords[1] = v1;
		triangleCoords[2] = v2;
		triangleCoords[3] = v3;
		triangleCoords[4] = v4;
		triangleCoords[5] = v5;
		triangleCoords[6] = v6;
		triangleCoords[7] = v7;
		triangleCoords[8] = v8;
 
		vertexBuffer.Put(triangleCoords);
		// set the buffer to read the first coordinate
		vertexBuffer.Position(0);
	}
 
	public void Draw (float[] mvpMatrix, float[] mvMatrix, float[] lightDirInEyeSpace)
	{
		// Add program to OpenGL environment
		GLES20.GlUseProgram (mProgram);
 
		// get handle to vertex shader's vPosition member
		mPositionHandle = GLES20.GlGetAttribLocation (mProgram, "vPosition");
 
		// Enable a handle to the triangle vertices
		GLES20.GlEnableVertexAttribArray (mPositionHandle);
		// Prepare the triangle coordinate data
		GLES20.GlVertexAttribPointer (mPositionHandle, COORDS_PER_VERTEX,
			GLES20.GlFloat, false,
			vertexStride, vertexBuffer);
 
		// get handle to shape's transformation matrix
		mMVPMatrixHandle = GLES20.GlGetUniformLocation (mProgram, "uMVPMatrix");
		MyGLRenderer.CheckGlError ("glGetUniformLocation");
		// Apply the projection and view transformation
		GLES20.GlUniformMatrix4fv (mMVPMatrixHandle, 1, false, mvpMatrix, 0);
		MyGLRenderer.CheckGlError ("glUniformMatrix4fv");
 
		//-
 
		mMVMatrixHandle = GLES20.GlGetUniformLocation (mProgram, "u_MVMatrix");
		MyGLRenderer.CheckGlError ("glGetUniformLocation");
		// Apply the projection and view transformation
		GLES20.GlUniformMatrix4fv(mMVMatrixHandle, 1, false, mvMatrix, 0);
		MyGLRenderer.CheckGlError ("glUniformMatrix4fv");
 
		// get handle to shape's transformation matrix
		mLightDirHandle = GLES20.GlGetUniformLocation (mProgram, "u_LightDir");
		MyGLRenderer.CheckGlError ("glGetUniformLocation");
 
		// Apply the projection and view transformation
		GLES20.GlUniform3f(mLightDirHandle, lightDirInEyeSpace[0], lightDirInEyeSpace[1], lightDirInEyeSpace[2] );
		MyGLRenderer.CheckGlError ("GlUniform4f");
 
		// get handle to normal
		mNormalHandle = GLES20.GlGetUniformLocation (mProgram, "a_Normal");
		MyGLRenderer.CheckGlError ("glGetUniformLocation");
 
		// calc normal
		float x = (triangleCoords[4] - triangleCoords[1]) * (triangleCoords[8] - triangleCoords[2]) - (triangleCoords[5] - triangleCoords[2]) * (triangleCoords[7] - triangleCoords[1]);
		float y = (triangleCoords[5] - triangleCoords[2]) * (triangleCoords[6] - triangleCoords[0]) - (triangleCoords[3] - triangleCoords[0]) * (triangleCoords[8] - triangleCoords[2]);
		float z = (triangleCoords[3] - triangleCoords[0]) * (triangleCoords[7] - triangleCoords[1]) - (triangleCoords[4] - triangleCoords[1]) * (triangleCoords[6] - triangleCoords[0]);
 
		// Apply the projection and view transformation
		GLES20.GlUniform3f(mNormalHandle, x, y, z );
		MyGLRenderer.CheckGlError ("GlUniform3f");
 
		//-
 
		// Draw the triangle
		GLES20.GlDrawArrays(GLES20.GlTriangles, 0, vertexCount);
		//GLES20.GlDrawElements (GLES20.GlTriangles, drawOrder.Length,
		//	GLES20.GlUnsignedShort, drawListBuffer);
 
		// Disable vertex array
		GLES20.GlDisableVertexAttribArray (mPositionHandle);
	}

My shaders are:

uniform mat4 uMVPMatrix;
uniform mat4 u_MVMatrix;
uniform vec3 u_LightDir;
 
attribute vec4 vPosition;
uniform vec4 a_Color; 
uniform vec3 a_Normal;
 
varying vec3 v_Color;
vec3 materialColor; 
 
void main()                    
{                              
    materialColor = vec3(1,0,0);
    vec4 newNormal = u_MVMatrix * vec4(a_Normal, 0.0); 
    float lightIntensity = max(0.0, dot(newNormal.xyz, u_LightDir));
    v_Color = materialColor * lightIntensity; 
 
    gl_Position = uMVPMatrix * vPosition;
}
 
 
precision mediump float; 
 
varying vec3 v_Color;          
 
void main()                    
{                              
   gl_FragColor = vec4(v_Color, 1.0); 
   gl_FragColor = v_Color; 
};

Triangles are not displayed with the right shading.

Could you help me ? Thank you in advance.

Keven Corazza

What output did you expect and what you’ve got instead?

I need a simple thing: I’ve some triangles that could represent a surface. I need to have a shading in order to understand the shape of the surface otherwise everything seems flat.

In the example there are two triangles one on the plane formed by axis x-y and one on the plane formed by axis y-z.

One triangle is everytime black, the other assume red gradation according to the rotation of the view but the result is not exactly the expected.

I don’t know if the problem is in vertex shader or if it caused by something else.

I would like to attach an image but it seems no possible inthis forum.

Thank you in advance.

Keven Corazza

Are your normals normalized?

I’ve just modified the shader in this way:

			+ " vec4 newNormal = u_MVMatrix * vec4(normalize(a_Normal), 0.0); 
"
			+ " float lightIntensity = max(0.0, dot(newNormal.xyz, u_VectorToLight)); 
"
			+ " v_Color = materialColor * lightIntensity; 
"

Normals are now normalized. But the result doesn’t change.

Other ideas ? Matrixes are right ?

Keven Corazza

Using a debugger might be a good idea.
Otherwise, hard to tell at a glance without seeing the output.

Also, make sure that the light direction vector is normalized and that its w component is set to zero prior to multiplying it with matrices.

Do you know where I can find a simple example that do what I need ?

I don’t need textures or other complex things. Just view triangle surfaces.

Thank you in advance.