Need help implementing 2-sided lighting using OpenGL GLSL
#1
Posted 30 November 2006 - 05:23 PM
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
Posted 30 November 2006 - 07:39 PM
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.
#3
Posted 01 December 2006 - 12:13 PM
/* 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
Posted 01 December 2006 - 09:31 PM
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);
#5
Posted 02 December 2006 - 05:53 AM
#6
Posted 03 December 2006 - 10:03 PM
#7
Posted 06 December 2006 - 03:26 PM
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
Posted 06 December 2006 - 04:38 PM
#9
Posted 06 December 2006 - 05:13 PM
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
Posted 06 December 2006 - 07:04 PM
Shree said:
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
Posted 06 December 2006 - 08:18 PM
Shree said:
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.
#12
Posted 06 December 2006 - 09:57 PM
Reedbeta said:
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
Posted 06 December 2006 - 11:09 PM
Shree said:
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.
#14
Posted 11 December 2006 - 03:22 PM
Reedbeta 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 ?
#15
Posted 11 December 2006 - 03:24 PM
Thanks for your helpful comments !
#16
Posted 11 December 2006 - 05:37 PM
Shree said:
Yes, I had only imagined a 2-level hierarchy but it could also be useful to have more levels.
#17
Posted 12 December 2006 - 05:43 PM
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












