Jump to content


Need help implementing 2-sided lighting using OpenGL GLSL


  • You cannot reply to this topic
16 replies to this topic

#1 Shree

    Member

  • Members
  • PipPip
  • 31 posts

Posted 30 November 2006 - 05:23 PM

I am trying to light (color) a 2 sided surface using a shader written in OpenGL GLSL but I can find no example code which does this and I am unsure how to go about it.

Can anyone help ?

I have implemented Phong shading in the GLSL shader but it only lights one of the surfaces so the 2-sided topology is not visible.

The image below (rendered using OpenGL) is what the structure should look like :
http://ccs.chem.ucl....-128%5e3-GL.png

With simple shading (rendered using Cg) it looks like this :
http://ccs.chem.ucl....8%5e3-v0-Cg.png

With Phong shading (rendered using GLSL) it looks like this :
http://ccs.chem.ucl....5e3-v0-GLSL.png

As you can see - only the "outer" blue surface is correctly colored.
The "inner" red surface is not visible.

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 30 November 2006 - 07:39 PM

First of all, make sure that back-face culling's disabled.
Second, just test in your shader whether the normal dotted with the view vector is negative. If it is, you're viewing the backside of this triangle, so flip the normal and change the color to red, then proceed with the ordinary lighting calculation.
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 Shree

    Member

  • Members
  • PipPip
  • 31 posts

Posted 01 December 2006 - 12:13 PM

Thanks for that advice ! I added the following code to my shader :

    
/* Use the half vector to obtain the (normalized) view direction */ 
    halfVec = normalize(vec3(gl_LightSource[0].halfVector.xyz));
    viewDir = halfVec - lightDir;

    if (dot(normal, viewDir) < 0.0)
    {
        normal = -normal; /* invert normal */
        gl_FrontColor = innerColour; /* e.g.red */
    }
    else
    {
        gl_FrontColor = outerColour; /* e.g. blue */
    }

and the resulting image now looks like this
ccs.chem.ucl.ac.uk/~shree/gyroid-128%5e3-v0-GLSL-2sided.png

So it works ! Both colours are now visible. However, the surfaces are no longer clearly defined, which defeats the point of the visualisation.

Is this because I have used a simple (crude ?) method to perform the 2 sided lighting ? Is there a way to improve the quality of the resultant coloring ?

#4 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 01 December 2006 - 09:31 PM

Well, you haven't used the Phong lighting equation at all. You need to scale the color by the dot product of the normal vector and light vector, just like you would in the ordinary case.

vec4 color;
if (dot(normal, viewVec) < 0.0)
{
    normal = -normal;
    color = innerColor;
}
else
{
    color = outerColor;
}
// add ambient and specular terms if you want
gl_FrontColor = color * dot(normal, lightVec);

reedbeta.com - developer blog, OpenGL demos, and other projects

#5 juhnu

    Valued Member

  • Members
  • PipPipPip
  • 292 posts

Posted 02 December 2006 - 05:53 AM

You most probably don't and won't need 2-sided lighting. Just make your geometry so that is has triangles facing both directions and you can avoid all the hassle associated with making support for the special case of 2-sided lighting. The extra overhead by having a few more triangles is meaningless in many cases.

#6 JeGX

    New Member

  • Members
  • Pip
  • 5 posts

Posted 03 December 2006 - 10:03 PM

You can also use the boolean gl_FrontFacing (GL2.0) to know if the current fragment being processed belongs to the front or the back face. But watch out, gl_FrontFacing seems to be properly supported on geforce but no on radeon (it switches back to software mode).

#7 Shree

    Member

  • Members
  • PipPip
  • 31 posts

Posted 06 December 2006 - 03:26 PM

Thanks for the tips guys - they were very helpful.

At the moment I am trying to speed-up the existing software rendering (with OpengGL) by the *addition* of a shader program. I can't change the scene geometry without making changes to the application code, which I am trying to avoid.

However, I will bear in mind the advice about drawing triangles facing in both directions to avoid the need for 2-sided lighting - especially as the image seems to render SLOWER now than it did before ! This is not an empirical difference as I have not taken any measurements yet but rotating the image is noticeably less smooth than it used to be.

I'd expect that using a shader to perform the coloring should (marginally) speed things up, so is the slower rendering caused by the addition of 2-sided lighting ? I'd guess that OpenGL has optimised this lighting operation, whereas my crude shader implementation of it might be causing a rendering bottleneck ?

#8 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 06 December 2006 - 04:38 PM

Hmm, I'm not sure what you mean when you say "the existing software rendering (with OpenGL)". OpenGL uses a graphics card as much as it can. How were you doing 2-sided lighting before? I would expect a GPU with a shader to perform faster than CPU rasterization, but not faster than a GPU with one-sided lighting and extra geometry.
reedbeta.com - developer blog, OpenGL demos, and other projects

#9 Shree

    Member

  • Members
  • PipPip
  • 31 posts

Posted 06 December 2006 - 05:13 PM

The code I am working with was written to visualise the data using OpenGL (i.e. software rendering).

For example, in the existing "render" function, built-in OpenGL functions were being used to perform the 2-sided lighting :

    
    if (colouredFlag) {
            glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
        } else {
            glColorMaterial(GL_BACK,GL_AMBIENT_AND_DIFFUSE);
            glColor3fv(innerColour);
            glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
            glColor3fv(outerColour);
        }

I am *adding* shader programs (written in both GLSL and Cg) to perform this coloring on the GPU so that I can do a comparison of rendering times to see how much faster it is (if at all) using the shaders.

Additionally, once I have the shaders working, I intend to add a Color Lookup Table (CLUT) so that the color scheme can be changed "dynamically" by the shader without having to reload scene geometry each time.

EDIT : After cleaning up my code the shader is now running (visibly at least) as fast as the software rendering, so I think the slow-down was caused by my poor coding rather than the 2-sided lighting.

I was doing this
    gl_FrontColor = color * NdotL * diffuse + globalAmbient + ambient; 
where
    diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;  
and
  NdotL = max(dot(normal, lightDir), 0.0); 


Removing the *diffuse* term solved the performance problem and corrected the coloring !

#10 geon

    Senior Member

  • Members
  • PipPipPipPip
  • 893 posts

Posted 06 December 2006 - 07:04 PM

Shree said:

visualise the data using OpenGL (i.e. software rendering).

Thatr is usually referred to as "hardware rendering". "Software rendering" means all rendering code is running on the CPU, Like in the good old days of MS-DOS gaming. ("Doom", "quake 1", "Hi Octane")

#11 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 06 December 2006 - 08:18 PM

Shree said:

The code I am working with was written to visualise the data using OpenGL (i.e. software rendering).

For example, in the existing "render" function, built-in OpenGL functions were being used to perform the 2-sided lighting :

    
    if (colouredFlag) {
            glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
        } else {
            glColorMaterial(GL_BACK,GL_AMBIENT_AND_DIFFUSE);
            glColor3fv(innerColour);
            glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
            glColor3fv(outerColour);
        }

This is still doing the 2-sided lighting in hardware; it's just using the fixed function pipeline rather than a shader. I would expect this code to be just as fast or faster than a shader. The only reason you would need to change this to a shader is if you wanted per-pixel lighting rather than per-vertex lighting. However, your geometry appears to be highly tesselated so I doubt this would make much visual difference. And you don't have to reload scene geometry to change the colors: just use glMaterialfv (not glColorMaterial) to directly set the ambient/diffuse coefficients for front and back faces.
reedbeta.com - developer blog, OpenGL demos, and other projects

#12 Shree

    Member

  • Members
  • PipPip
  • 31 posts

Posted 06 December 2006 - 09:57 PM

Reedbeta said:

This is still doing the 2-sided lighting in hardware; it's just using the fixed function pipeline rather than a shader. I would expect this code to be just as fast or faster than a shader. The only reason you would need to change this to a shader is if you wanted per-pixel lighting rather than per-vertex lighting. However, your geometry appears to be highly tesselated so I doubt this would make much visual difference.

Interesting......
It seems the initial premise of my work might be incorrect.

I'd assumed that using a shader program would be faster "per se" but from your reply it seems it depends on what type of lighting I am trying to implement and even what the scene geometry consists of.

This would suggest shaders are only needed for more complicated lighting operations and are written with a specific scene in mind. Is this indeed the case ?

Basically, I am trying to render larger datasets than can currently be visualised using the OpenGL code (fixed-function pipeline).

http://ccs.chem.ucl....-128%5E3-GL.png

The image linked above represents a 64 cubed dataset with each point having 1kbyte of data making 268 Mbytes in total. However, there are datasets which are 256 cubed or greater which contain upwards of several Gigabytes of data.

These cannot currently be rendered on a PC (even running the latest graphics card) because most don't have the RAM to hold the dataset in memory. I will have to use a PC cluster or even a shared-memory computer (e.g SGI Prism) but I had hoped that using shader programs in conjunction with this hardware would speed things up appreciably. This assumption now appears to be incorrect...

#13 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 06 December 2006 - 11:09 PM

Shree said:

This would suggest shaders are only needed for more complicated lighting operations and are written with a specific scene in mind. Is this indeed the case?

Basically, I am trying to render larger datasets than can currently be visualised using the OpenGL code (fixed-function pipeline).

Yes, shaders are primarily useful as a general purpose way to specify arbitrarily complicated operations to be performed per vertex or per pixel. They will not help you with rendering extremely large amounts of data, though.

I don't have a lot of experience working with extremely large data sets, but I would advise you to use a reduced version of the data set for real-time operations - like when you want to rotate the object and see it rotating in real time - and only render the whole data set when the user wants to, bearing in mind that it may take anywhere from several seconds to several minutes or longer to do this. You can do it by rendering a bit of the data, then unloading it from memory and loading in the next section and rendering it into the same image, and so forth. Of course you'd want to do all this in a separate thread so as not to make your application freeze while this is taking place. You can have a progress bar or something, and present the completed image at the end, or just have the card draw into the front buffer so that the user can see the image as it is being composed.
reedbeta.com - developer blog, OpenGL demos, and other projects

#14 Shree

    Member

  • Members
  • PipPip
  • 31 posts

Posted 11 December 2006 - 03:22 PM

Reedbeta said:

I don't have a lot of experience working with extremely large data sets, but I would advise you to use a reduced version of the data set for real-time operations - like when you want to rotate the object and see it rotating in real time - and only render the whole data set when the user wants to, bearing in mind that it may take anywhere from several seconds to several minutes or longer to do this.

This sounds like using varying Levels of Detail when visualising data, where the data is stored in a hierarchical data structure and only the highest level(s) are rendered when viewing the dataset as whole, but the user can select smaller subsets of the data to be rendered in more detail. Is this what you had in mind ?

#15 Shree

    Member

  • Members
  • PipPip
  • 31 posts

Posted 11 December 2006 - 03:24 PM

From the feedback that I received it appears that using a shader will not offer significant performance improvements so I will investigate parallelizing the existing iso-surfacing code instead.

Thanks for your helpful comments !

#16 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 11 December 2006 - 05:37 PM

Shree said:

This sounds like using varying Levels of Detail when visualising data, where the data is stored in a hierarchical data structure and only the highest level(s) are rendered when viewing the dataset as whole, but the user can select smaller subsets of the data to be rendered in more detail. Is this what you had in mind ?

Yes, I had only imagined a 2-level hierarchy but it could also be useful to have more levels.
reedbeta.com - developer blog, OpenGL demos, and other projects

#17 Shree

    Member

  • Members
  • PipPip
  • 31 posts

Posted 12 December 2006 - 05:43 PM

Ok, I am now seeing some strange behaviour with my 2 sided lighting ! If anyone has any ideas as to what is happening your comments would be most welcome...

The "front-view" now looks great :yes:
However, when I rotate the image 180 degrees, the "back-view" looks inside-out ! :no:
As I rotate the image it is almost as if the back facing sides are "erased" as seen here : :surprise:

Here is the full code of my vertex shader for completeness :

uniform vec4 innerColour;
uniform vec4 outerColour;
uniform vec4 surfaceColour;
uniform bool invertColour;

void main()
{
    vec3 normal, lightDir;
    vec3 viewDir, halfVec;
    vec4 color, diffuse, ambient, globalAmbient;
    float NdotL;
    
    /* First transform the normal into eye space and normalize the result */
    normal = normalize(gl_NormalMatrix * gl_Normal);

    /* Now normalize the light's direction. Note that according to the
    OpenGL specification, the light is stored in eye space. Also since 
    we're talking about a directional light, the position field is actually 
    direction */
    lightDir = normalize(vec3(gl_LightSource[0].position));
    
    /* 2-sided lighting */
    /* Use the half vector to obtain the (normalized) view direction */ 
    halfVec = normalize(vec3(gl_LightSource[0].halfVector.xyz));
    viewDir = halfVec - lightDir;

    if (dot(normal, viewDir) < 0.0) 
    {
        color = outerColour; /* blue */
    }
    else
    {
        normal = -normal; /* invert normal */
        color = innerColour; /* red */
    }

    /* Compute the cos of the angle between the normal and lights direction. 
    The light is directional so the direction is constant for every vertex.
    Since these two are normalized the cosine is the dot product. We also 
    need to clamp the result to the [0,1] range. */
    NdotL = max(dot(normal, lightDir), 0.0);

    /* Compute the ambient and globalAmbient terms */
    ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
    globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient;
    
    gl_FrontColor = color * NdotL + globalAmbient + ambient;

    if (invertColour)
    {
        /* Invert the colour at the vertices */
        gl_FrontColor.r = 1.0-gl_FrontColor.r;
        gl_FrontColor.g = 1.0-gl_FrontColor.g;
        gl_FrontColor.b = 1.0-gl_FrontColor.b;
    }

    /* Optimised GLSL function for ModelViewProjectionMatrix transform */
    /* Equivalent to : gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; */
    /* and also : gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; */
    gl_Position = ftransform();
} 






1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users