I want to put a positional light in my scene. I expect distant objects to take less light, but opengl just care about angle between surface normal and light. Am I doing something wrong or do I have to add another function?
GLfloat lightIntensity=1.0;
GLfloat main_position[] = {0.0, 0.0, 1.0, 1.0};
GLfloat light_ambient[] = {0.2, 0.2, 0.2, 0.0};
GLfloat light_diffuse[] = {lightIntensity, lightIntensity, lightIntensity, 1.0};
GLfloat light_specular[] = {0.0, 0.0, 0.0, 0.0};
/* Enable a single OpenGL light. */
glLightfv(GL_LIGHT0, GL_POSITION, main_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
When I run the code, 2 object with same surface normal have same lighting even though they have different distance from the light source
For a distance dependent light you've to set the attenuation parameters GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION respectively GL_QUADRATIC_ATTENUATION. See glLight. By default the light is constant independent on the distance.
The light attenuation is specified in OpenGL 2.0 Specification - 2.14.1 Lighting, page 62.
The light attenuation factor is defiend as:
att = 1 / (kc + kl * d + kq * d * d)
d ... distance from the light source to the fragment
kc ... constant attenuation
kl ... linear attenuation
kq ... quadratic attenuation
The default value for the constant attenuation is 1, for the linear and quadratic attenuation it is 0. This leads to a distance independent factor of 1.
e.g. In the following a attenuation is set, where the attenuation factor is <= 1.0/255.0 at a distance of max_dist:
float max_dist = ...;
float threshold = 1.0f/255.0f;
float kc = 0.0f;
float kq = 0.0f;
float kl = (1.0f/threshold - kc - kq*max_dist*max_dist) / max_dist;
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, kc);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, kl);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, kq);
For quadratic attenuation the same can be achieved by
float kc = 0.0f;
float kl = 0.0f;
float kq = (1.0f/threshold - kc - kl*max_dist) / (max_dist*max_dist);
Related
I am not quite sure what is missing, but I loaded a uniform matrix into a vertex shader and when the matrix was:
GLfloat translation[4][4] = {
{1.0, 0.0, 0.0, 0.0},
{0.0, 1.0, 0.0, 0.0},
{0.0, 0.0, 1.0, 0.0},
{0.0, 0.2, 0.0, 1.0}};
or so, I seemed to be able to translate vertices just fine, depending on which values I chose to change. However, when swapping this same uniform matrix to apply projection, the image would not appear. I tried several matrices, such as:
GLfloat frustum[4][4] = {
{((2.0*frusZNear)/(frusRight - frusLeft)), 0.0, 0.0, 0.0},
{0.0, ((2.0*frusZNear)/(frusTop - frusBottom)), 0.0 , 0.0},
{((frusRight + frusLeft)/(frusRight-frusLeft)), ((frusTop + frusBottom) / (frusTop - frusBottom)), (-(frusZFar + frusZNear)/(frusZFar - frusZNear)), (-1.0)},
{0.0, 0.0, ((-2.0*frusZFar*frusZNear)/(frusZFar-frusZNear)), 0.0}
};
and values, such as:
const GLfloat frusLeft = -3.0;
const GLfloat frusRight = 3.0;
const GLfloat frusBottom = -3.0;
const GLfloat frusTop = 3.0;
const GLfloat frusZNear = 5.0;
const GLfloat frusZFar = 10.0;
The vertex shader, which seemed to apply translation just fine:
gl_Position = frustum * vPosition;
Any help appreciated.
The code for calculating the perspective/frustum matrix looks correct to me. This sets up a perspective matrix that assumes that your eye point is at the origin, and you're looking down the negative z-axis. The near and far values specify the range of distances along the negative z-axis that are within the view volume.
Therefore, with near/far values of 5.0/10.0, the range of z-values that are within your view volume will be from -5.0 to -10.0.
If your geometry is currently drawn around the origin, use a translation by something like (0.0, 0.0, -7.0) as your view matrix. This needs to be applied before the projection matrix.
You can either combine the view and projection matrices, or pass them separately into your vertex shader. With a separate view matrix, containing the translation above, your shader code could then look like this:
uniform mat4 viewMat;
...
gl_Position = frustum * viewMat * vPosition;
First thing I see is that the Z near and far planes is chosen at 5, 10. If your vertices do not lie between these planes you will not see anything.
The Projection matrix will take everything in the pyramid like shape and translate and scale it into the unit volume -1,1 in every dimension.
http://www.lighthouse3d.com/tutorials/view-frustum-culling/
If the cone angle is over 90 degrees, my spot light's angular attenuation is not working correctly. From 0.1 to 90, the attenuation is smooth from the center of the cone to the edges, but from 90 to 179.9, it gets sharper and sharper edges.
Here's my attenuation code:
uniform vec3 lightPosition; // Light's position
uniform vec3 lightDirection; // Light's direction
uniform float lightAngleCos: // Cosine of the half of the cone angle
uniform float lightRange: // Light's range
// Get the light vector
vec3 pixelToLight = lightPosition - position.xyz;
vec3 normPTL = normalize(pixelToLight);
// Get the dot product between the light direction and the light vector
float rho = dot(normPTL, -lightDirection);
if(rho > lightAngleCos)
{
float dif = 1.0 - lightAngleCos;
float angularAttenuation = clamp((rho - lightAngleCos) / dif, 0.0, 1.0);
float radialAttenuation = 1.0 - clamp(length(pixelToLight) / (lightRange), 0.0, 1.0);
float attenuation = angularAttenuation * radialAttenuation;
// Apply attenuation
out_color = color * attenuation;
}
Calc it in actual angles, not in cos, since cos is not linear, so you had very smooth attenuation gradient near 0, and very sharp near 180, you can see that just by looking at cos graph near 0 and near Pi/2.
In code you should calc:
rhoAngle = acos(rho);
lightAngleCos = acos(lightAngleCos);
and then use it to calc attenuation:
float dif = Pi/2.0 - lightAngle;
float angularAttenuation = clamp((lightAngle - rhoAngle) / dif, 0.0, 1.0);
My original solution was correct altogether. It was my cone mesh that caused limitations.
Well, I've been wrestling with implementing per-pixel lighting for a few days, and this is essentially the "result" I generally end up with.
I have these hard black spots throughout my mesh, and also those off-kilter dark spots. The blue shading "kind of" works right, except that it applies itself throughout the entire mesh and it appears to apply itself randomly as seen in the image. As I've bound the light to my camera, the light DOES "travel" across the mesh, albeit oddly most of the time. I have no idea why this is happening; my mesh data has normals that, as far as I can tell, are fine (no colour/smoothing issues in MilkShape, 3DS, Lightwave, Blender, Maya etc).
Here's my setup/light code:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
float lpos[4] = {get_cam_pos().x,get_cam_pos().y,get_cam_pos().z,1};
float lamb[4] = {0,0,1,1};
float ldiff[4] = {1,1,0,1};
float lspec[4] = {1,1,0.5,1};
GLfloat lin[4] = {5.0f,5.0f,5.0f,1};
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_POSITION, lpos);
glLightfv(GL_LIGHT0, GL_AMBIENT, lamb);
glLightfv(GL_LIGHT0, GL_DIFFUSE, ldiff);
glLightfv(GL_LIGHT0, GL_SPECULAR, lspec);
<from here camera matrix is then loaded and scene is rendered>
And here is my vert shader, from the Lighthouse3D per-pixel-light tutorial:
varying vec4 diffuse,ambientGlobal,ambient, ecPos;
varying vec3 normal,halfVector;
varying float dist;
void main()
{
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
/* first transform the normal into eye space and normalize the result */
normal = normalize(gl_NormalMatrix * gl_Normal);
/* compute the vertex position in camera space. */
ecPos = gl_ModelViewMatrix * gl_Vertex;
/* Normalize the halfVector to pass it to the fragment shader */
halfVector = gl_LightSource[0].halfVector.xyz;
/* Compute the diffuse, ambient and globalAmbient terms */
diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
ambientGlobal = gl_LightModel.ambient * gl_FrontMaterial.ambient;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
...and the frag shader, also from the Lighthouse3D tutorial:
uniform sampler2D color_texture;
varying vec4 diffuse,ambientGlobal, ambient, ecPos;
varying vec3 normal,halfVector;
varying float dist;
void main()
{
vec4 tex0Color = vec4(texture2D(color_texture,gl_TexCoord[0].st));
vec3 n,halfV,viewV,lightDir;
float NdotL,NdotHV;
vec4 color = ambientGlobal;
float att;
/* a fragment shader can't write a verying variable, hence we need
a new variable to store the normalized interpolated normal */
n = normalize(normal);
// Compute the ligt direction
lightDir = vec3(gl_LightSource[0].position-ecPos);
/* compute the distance to the light source to a varying variable*/
dist = length(lightDir);
/* compute the dot product between normal and ldir */
NdotL = max(dot(n,normalize(lightDir)),0.0);
if (NdotL > 0.0) {
att = 1.0 / (gl_LightSource[0].constantAttenuation +
gl_LightSource[0].linearAttenuation * dist +
gl_LightSource[0].quadraticAttenuation * dist * dist);
color += att * (diffuse * NdotL + ambient);
halfV = normalize(halfVector);
NdotHV = max(dot(n,halfV),0.0);
color += att * gl_FrontMaterial.specular * gl_LightSource[0].specular
* pow(NdotHV,gl_FrontMaterial.shininess);
}
gl_FragColor = tex0Color*color;
}
Your normals look wrong to me ...
;-)
I've got an openGL 3d scene with two simple objects (glutSolidCube and glutSolidTeapot). When I set up the lights with GL_COLOR_MATERIAL enabled, I get the following result:
Which is good. Then when I set up my own material like this:
//diffuse light color variables
GLfloat dlr = 0.4;
GLfloat dlg = 0.6;
GLfloat dlb = 0.9;
//ambient light color variables
GLfloat alr = 0.7;
GLfloat alg = 0.7;
GLfloat alb = 0.7;
//ambient light color variables
GLfloat slr = 0.4;
GLfloat slg = 0.4;
GLfloat slb = 0.4;
GLfloat DiffuseLight[] = {dlr, dlg, dlb}; //set DiffuseLight[] to the specified values
GLfloat AmbientLight[] = {alr, alg, alb}; //set AmbientLight[] to the specified values
GLfloat SpecularLight[] = {slr, slg, slb}; //set AmbientLight[] to the specified values
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (float *)&AmbientLight);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (float *)&DiffuseLight);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (float *)&SpecularLight);
I get this very different result, in which you can see it's not being shaded properly, it's like FLAT shading although I defined it as SMOOTH (Gouraud).
Where can the problem be? Is it on the material definition?
You forgot to set specular shininess.
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 12.0f);
Set it to 10...25 and it'll look much better/shinier. It won't look as good as per-pixel lighting, though. Default value for shininess is zero which will look exactly like what you see - i.e. ugly.
I get this very different result, in which you can see it's not being shaded properly, it's like FLAT shading although I defined it as SMOOTH (Gouraud).
Well, you got smooth shading. However the OpenGL fixed function pipeline evaluates illumination values only at the vertices, then performs barycentric interpolation over the face. The result you got is exactly what to expect.
What you want is per pixel/fragment illumination. Only way to get this is by using a shader (well, it's also possible by tinkering with the so called "texture combiner environment", but getting that one to work properly is a lot of hard work. Implementing a Phong illumination shader is a matter of minutes).
By changing your lighting and material settings you're just putting emphasis on the shortcommings of the Gouraud shading model.
I'm having a problem with lighting when dealing with really small particles. I'm doing particle-based fluid simulation and am right now rendering the fluid as really tiny polygonized spheres (by really tiny I'm saying about 0.03 units radius for the spheres). The lighting in my scene isn't lighting them how I want and I can't get it to look right. I'm looking for something similar to the soft lighting on the particles in this image...
However my particles look like this...
See how my particles have bright white sections whereas the green particles are just lit up softly and don't have large white hotspots. I know the reason is either the settings for my light or simply the fact that the particles are so small that the light takes up a larger space (is that possible??). My settings for lighting are as follows...
GLfloat mat_ambient[] = {0.5, 0.5, 0.5, 1.0};
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
GLfloat mat_shininess[] = {10.0};
GLfloat light_position[] = {0.0, 0.1, p_z, 1.0};
GLfloat model_ambient[] = {0.5, 0.5, 0.5, 1.0};
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, model_ambient);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
Thanks for all the suggestions everyone but unfortunately nothing worked. I sat down with my graphics professor and we determined that this problem was in fact related to the size of the particles and the fact that OpenGL treats directional lights as being infinitely far away from any vertex. The proper way to fix it was modifying the constant attenuation of the light source like this...
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 10.0);
Now my particles look like this...
which is exactly the lighting I was after!
The size of the particles isn't an issue - you're over-saturating your colours.
For each RGB component, you should have ambient + diffuse + specular <= 1.0
For a scene like this I'd expect ambient to be no more than 0.1 or so, diffuse of 0.6 or so, and specular making up the rest.
It looks like you need to turn down the specular component of your material, turn down the ambient a bit, and add some diffuse shading (GL_DIFFUSE). Consider also positioning the light behind the the viewport/camera.