1

I want to know how to calculate per-fragment normals to be able to add light to the scene!

I read in a texture generated from libnoise library - http://libnoise.sourceforge.net and create a terrain by calculating stuff in geometry-shader as seen below

The terrain looks fine but not the lightning..

Want to calculate normals for light

My code looks like this:

const int TOTAL = (TERRAIN_WIDTH*TERRAIN_DEPTH);
const int TOTAL_INDICES = TOTAL*3*3;
glm::vec3 vertices[TOTAL];
GLuint indices[TOTAL_INDICES];

//...

int count = 0;
GLuint* id=&indices[0];
// Set up Geometry for terrain
for(int j = 0; j < TERRAIN_DEPTH; j++) {
    for(int i = 0; i < TERRAIN_WIDTH; i++) {

    //So I want to calculate position and normal here <-----
    vertices[count] = glm::vec3((float(i)/(TERRAIN_WIDTH-1)), 1.0,(float(j)/(TERRAIN_DEPTH-1)));
    count++;
    }
}

for (int i = 0; i < TERRAIN_DEPTH-1; i++) {
    for (int j = 0; j < TERRAIN_WIDTH-1; j++) {
        int i0 = j+ i*TERRAIN_WIDTH;
        int i1 = i0+1;
        int i2 = i0+TERRAIN_WIDTH;
        int i3 = i2+1;

        *id++ = i0; 
        *id++ = i2; 
        *id++ = i1; 
        *id++ = i1; 
        *id++ = i2; 
        *id++ = i3; 
    }    
}

...

GLubyte *pData = SOIL_load_image(filename, &textureWidth, 
                            &textureHeight, &channels, SOIL_LOAD_L);

glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) , &vertices[0], GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementArrayObject);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), &indices[0], GL_STATIC_DRAW);

glGenTextures(1, &heightMapTextureID);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, heightMapTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, textureWidth, textureHeight, 0, GL_RED, GL_UNSIGNED_BYTE, pData);

Geometry shader:

layout (triangles) in;
layout (triangle_strip, max_vertices=9) out; 

uniform sampler2D heightMapTexture;
uniform mat4 projection;
uniform mat4 model;
uniform mat4 view;


void main()
{
    for(int i=0;i<gl_in.length(); i++) {
        vec4 position = gl_in[i].gl_Position;

        float height = texture(heightMapTexture, position.xz).r;
        vec2 xz = (position.xz*50); // the multiplication will decide how big the terrain will be
        gl_Position =  (projection * view * model) * vec4(xz.x,height*5,xz.y , 1.0);            
        EmitVertex();
    }
    EndPrimitive();
}

And my vertex and fragment shader are as basic as it can get.

#version 330 core
layout(location = 0) out vec4 finalColor;

void main()
{   
    finalColor = vec4(vec3(1,1,1), 1.0f);
}

Vertex:

#version 330 core
//inputs
layout(location = 0)in vec3 position;

void main()
{
    gl_Position =  vec4(position, 1.0f);
}

Can someone help me?

genpfault
  • 51,148
  • 11
  • 85
  • 139
Kahin
  • 455
  • 2
  • 9
  • 21
  • 1
    The simplest way to compute per-fragment normals is to take the cross product of the partial derivatives of `gl_FragCoord` in the fragment shader. That will get you screen space normals. This is discussed [here](http://stackoverflow.com/questions/17528878/compute-normals-from-displacement-map-in-three-js-r-58/17532576#17532576). Those normals will be flat though, so I don't know if that's what you want? And that is using view-space normals, which requires interpolating an extra per-vertex attribute. _I'm not sure which coordinate space normals you need for your application._ – Andon M. Coleman Mar 01 '15 at 13:01

1 Answers1

0

You should calculate the normals on your geometry before passing that to your terrain. I would say its more effort now, but it would be better to generate all the data for your terrain CPU side and then pass that to your graphics card. The problem is that one fragment has no information about the next fragment or the previous fragment.

You should pass your normal vertex data, along with normals and colour(or textures, or neither) and then use that to do your calculations on the GPU.

The thing is that, yes you can generate geometry in your GeometryShader, but you wont be able to get smooth lighting because you can only calculate one normal for a face.

So in summary:

1.) Generate your geometry on your CPU and store it there as well.

2.) Iterate over each vertex and calculate its normal. To calculate its normal, you will take the normal of all neighbouring faces or vectors then sum them and take the mean.

3.) Pass this data to your shader pipeline.

4.) Calculate your lighting model(Presumably phong) based on all your inputs.

This also means you don't have to generate geometry each frame which can become quite expensive if your terrain or world become much larger.

Daniel
  • 589
  • 4
  • 19