Question about calculating vertex normals

Hi. This question is about calculating the normal of a vertex in GLSL (after I have moved the given vertex). For reference, I am posting the sample code from the OpenGL Shading Language 2nd Edition book, from Ch 16.6, Listing: 16.3.

My questions are:

  1. I need to recalculate the normal after modifying the vertex position, right? (Assume that I have a cube and the vertices get moved +noise.xyz. Then I believe that the given normals for each vertex will be incorrect and need to be recalculated.) If I’m wrong, please tell me why. If I’m correct, can someone please point to a simple example, calculation, or GLSL function to calculate the new normal? (Note: assume I can calculate the position of neighboring vertices.)

  2. If I need to recalculate the vertex-normal (question 1 above), then should I store the normal in gl_Normal or somewhere else? I thought the vertex-normal was to be used in some fixed-function pipeline calculations for interpolation… (but I might be confused with bump map/texture normal interpolations in the fixed-function pipeline).

Code from book:


uniform vec3  LightPosition;
uniform vec3  SurfaceColor;
uniform vec3  Offset;
uniform float ScaleIn;
uniform float ScaleOut;
varying vec4  Color;

void main()
{
    vec3 normal = gl_Normal;
    vec3 vertex = gl_Vertex.xyz +
                  noise3(Offset + gl_Vertex.xyz * ScaleIn) * ScaleOut;

    // Posting question 1: this normal I think is wrong, dont we need to recalculate first?
    normal = normalize(gl_NormalMatrix * normal);
    vec3 position = vec3(gl_ModelViewMatrix * vec4(vertex,1.0));
    vec3 lightVec = normalize(LightPosition - position);
    float diffuse = max(dot(lightVec, normal), 0.0);

    if (diffuse < 0.125)
         diffuse = 0.125;

    Color = vec4(SurfaceColor * diffuse, 1.0);

    // Posting question 2: dont we need to save the new vertex normal for future calculations
    gl_Position = gl_ModelViewProjectionMatrix * vec4(vertex,1.0);
}

Thanks.

If you move mesh vertices, you noeed to compute the new surface normals.
gl_Normal, is just the vertex normal in object space you give in the opengl application with glNormal fucntion or in a vertex array.

If you transform vertices position with a parametric deformation function, I suggest you to read this article from GPU gems. It would help you to compute the new normals:
http://http.developer.nvidia.com/GPUGems/gpugems_ch42.html

maybe you find something here:
http://www.ozone3d.net/tutorials/mesh_deformer.php

Strangely enough, I’ve been working on the exact same shader code. I’ve been attempting to generate normals using the ‘neighbour’ technique outlined by tonfilm in this post on his blog.

I’ve had limited success so far. Here is my vertex shader code as it stands currently:


//
// vertexnoise.vert: Vertex shader for warping the geometry with noise.
//
// author: Philip Rideout
//
// Copyright (c) 2005-2006: 3Dlabs, Inc.
//
//
// See 3Dlabs-License.txt for license information
//

// Lighting + surface controls
uniform vec4 AmbientColor, DiffuseColor;
uniform vec3 LightPosition;
uniform vec4 SurfaceColor;

///////////////////////////
//       Functions       //
///////////////////////////

// Varying-type variable for lighting color, sent to Fragment Shader.
// Varyings are interpolated across polygons to give a smooth result in the FS.
varying vec4 outColor;

// Environment-Map function
void envMapVS(in vec4 vert, in vec3 norm)
{
	vec4 vWorld = gl_ModelViewMatrix * vert;
	vec3 nWorld = gl_NormalMatrix * norm;
	
	// Diffuse light
	vec3 vertToLight = normalize(LightPosition - vWorld.xyz);
	float diffuseLight = max(dot(vertToLight, nWorld), 0.0);
	
	// This varying variable is passed to the Fragment Shader
	outColor = AmbientColor + vec4(diffuseLight * DiffuseColor.xyz, DiffuseColor.w);
	
	// Environment mapping texture coordinates
	vec3 vWorldUnit = normalize(vWorld.xyz);
	vec3 f = reflect(vWorldUnit, nWorld);
	float m = 2.0 * sqrt(f.x * f.x + f.y * f.y + (f.z + 1.0) * (f.z + 1.0));
	
	// Texture coordinates set
	// (determines which part of  envMap to lookup in FS).
	// Also automatically interpolated between VS and FS.
	gl_TexCoord[0].xy = vec2(f.x / m + 0.5, -f.y / m + 0.5);
}

// 3D Noise controls
uniform vec3 Offset;
uniform float ScaleIn;
uniform float ScaleOut;

// 3D Noise function
vec4 noise3D(in vec4 vert, in vec3 gridOffset)
{
	vert.xyz += noise3(gridOffset + Offset + vert.xyz * ScaleIn) * ScaleOut;
	return vert;
}

// Structure to hold vertex position and normal
struct posNorm {vec4 pos;vec3 norm;};

// Calculate and return vertex position and normal
posNorm vNoise3D(in vec4 vert)
{
	// Init output variable of custom type posNorm (defined above)
	posNorm result;
	
	// Calculate new vertex position using function defined above
	result.pos = noise3D(vert, vec3(0.0));
	
	// Calculate normals
	float gridOffset	= 0.00001;
	vec4 neighbour1	= noise3D(vert, vec3(gridOffset, 0.0, 0.0));
	vec4 neighbour2	= noise3D(vert, vec3(0.0, gridOffset, 0.0));
	
	vec3 tangent	= neighbour1.xyz - result.pos.xyz;
	vec3 bitangent	= neighbour2.xyz - result.pos.xyz;
	
	vec3 norm	= cross(tangent, bitangent);
	norm		= normalize(norm);
	norm		= gl_NormalMatrix * norm;
	result.norm	= norm;
	
	return result;
}

///////////////////////////
//           Main Loop           //
///////////////////////////

void main(void)
{
	// Initial vertex position
	vec4 vertex = gl_Vertex;
	
	// get final vertex position and normals
	posNorm outPut = vNoise3D(vertex);
	
	// Call envMapVS function, passing it vertex position and normal
	vec3 normal = gl_NormalMatrix * outPut.norm;
	
	// Apply environment-map lighting function
	envMapVS(vertex, normal);
	
	gl_Position = gl_ModelViewProjectionMatrix * outPut.pos;
}

And this is the result:

Anyone any thoughts on where I might be going wrong?

Cheers,

a|x
http://machinesdontcare.wordpress.com

Incidentally, I don’t think the Jacobian Matrix approach would work in the case of using the noise() function. Wouldn’t you need to know the actual formula used for the Perlin noise function, in order to work out the derivatives for the given input? I know this approach works for relatively simple parametric formulae, but I wouldn’t have thought it would work in this case…

Or, am I wrong?

a|x

The function either needs to be known for the exact partial derivatives, or you could estimate them, which is essentially what you’re doing right now with the neighbor technique.

One thing to try would be to increase your step size - it looks a bit small. Another thing would be to try normalizing your tangent and binormal vectors before using them to compute the normal.

EDIT: I just realized that noise*() is being used. You might have more luck using your own noise function instead, so you know exactly what is going on.

Hi Seth_Hoffert,

the only vertex shader noise function I’ve seen was written in HLSL, and was based around a large constant array of floats (64, I think) used as a lookup table. Do you happen to know of any perlin-like noise implementations that can be used in a GLSL vertex shader? I’m not after a perfect perlin function, necessarily, but maybe one that’s nice and fast, and I can use to generate vertex positions and normals…

a|x

Sorry to dig this old thread up again, but I was wondering if anyone had any thoughts on how I might get the shader in my post above working, or if mrmoo, the author of the original post, had had any luck getting normal-generation working.

It looks like what I need to do is to flip the normals in the dark areas, but I can’t work out what conditional to use in order to work out when the normals need to be negated.

Could anyone tell me if I’m barking up the wrong tree with this? It could be that it’s not actually possible to generate normals for a perlin-noise-modulated mesh like this. The fact that I do seem to get smooth shading over parts of the surface would seem to suggest that it IS possible though. It seems to me that I just need to work out how to stop some of the normals being inverted, but I could be wrong…

Anyone any ideas what I should try?

a|x

I kind of put the normals-calculation to the side and started looking at GS stuff. As for flipping the normals, toneburst, you can try looking at this post. The user seems to be using the same nearby-neighbor-calculation method and problem.

Here’s a handy one

http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter26.html

Hi modus.

That does look handy. I’m not sure that would really help with my particular problem though. I’m sure there must be something basic I’m doing wrong, and that I should be able to use this normal-estimation method.

Hmm…

a|x