OpenGL Spotlights - c++

I'm trying to make 'spotlights' over a pool table in openGL. This should be fairly simple, but something is going wrong, and I can't work out what.
I have a class 'PoolLight' that I'm using as a sort of holding class for the lights. Here is is:
#include "PoolLight.h"
#include "Glut/glut.h"
#include "GL/gl.h"
#include "GL/glu.h"
PoolLight::PoolLight() {
}
PoolLight::PoolLight(GLenum lightNumber, GLenum lightType, float red, float green, float blue, bool distant, float posX, float posY, float posZ)
{
this->lightNumber = lightNumber;
this->lightType = lightType;
color[0] = red; color[1] = green; color[2] = blue; color[3] = 1;
position[0] = posX; position[1] = posY; position[2] = posZ; position[3] = (int) (!distant);
glLightfv(lightNumber, lightType, color);
glLightfv(lightNumber, GL_POSITION, position);
enabled(true);
}
PoolLight::~PoolLight(void)
{
}
void PoolLight::setSpotlight(float angle, float attenuation, float dirX, float dirY, float dirZ) {
glLightf(lightNumber, GL_SPOT_EXPONENT, angle);
glLightf(lightNumber, GL_CONSTANT_ATTENUATION, attenuation);
glLightf(lightNumber, GL_LINEAR_ATTENUATION, 0.0f);
glLightf(lightNumber, GL_QUADRATIC_ATTENUATION, 0.0f);
spotDirection[0] = dirX; spotDirection[1] = dirY; spotDirection[2] = dirZ;
glLightfv(lightNumber, GL_SPOT_DIRECTION, spotDirection);
glLightf(lightNumber, GL_SPOT_CUTOFF, 60);
}
void PoolLight::enabled(bool enabled) {
if (enabled) glEnable(lightNumber);
else glDisable(lightNumber);
}
void PoolLight::reposition() {
glLightfv(lightNumber, GL_POSITION, position);
}
And in Display::Init, I have this code:
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glPointSize(6);
//Lighting
middleSpotlight = PoolLight(GL_LIGHT0, GL_DIFFUSE, .3, .3, 0.15, false, 0, 0, 50);
middleSpotlight.setSpotlight(60, 1, 0, 0, -1);
upperSpotlight = PoolLight(GL_LIGHT1, GL_DIFFUSE, .3, .3, 0.15, false, 0, 45, 50);
upperSpotlight.setSpotlight(60, 1, 0, 0, -1);
lowerSpotlight = PoolLight(GL_LIGHT2, GL_DIFFUSE, .3, .3, 0.15, false, 0, -45, 50);
lowerSpotlight.setSpotlight(60, 1, 0, 0, -1);
However, even when disabling all but the middle spotlight my scene is uniformly lit with a sort of 'blanket' lighting.
I feel like I'm probably missing something obvious, but I just can't see what.

This is due to how OpenGL default fixed function pipeline implements lighting: Lighting is evaluated only at the vertices, the resulting colour interpolated over the surface. If your whole table consists of just one large quad, exactly this happens.
Solution 1: Subdivide the mesh to a high degree.
Solution 2: Replace fixed function lighting with a per-fragment-illumination shader.

Related

Multiple spotlights in opengl doesn't work

I'm using opengl for educational purposes, but I'm having trouble creating multiple spotlights to represent street lamps. I use an iterator to create several however in the end only the last spotlight gets the light, I believe the problem is in the addlight method however I do not know what is happening.
In image below you can see happen.
https://imgur.com/Y77eHln
#include "CreateLamps.h"
std::vector<SpotLight> lamp;
CreateLamps::CreateLamps(int number) {
for (int i = 0; i < number; i++) {
SpotLight *spot = new SpotLight(-8.0f, 5.f, (i*10) + 30.0f, 1.f);
lamp.push_back(*spot);
}
}
void CreateLamps::Add() {
std::vector<SpotLight>::iterator it = lamp.begin();
while (it != lamp.end())
{
glPushMatrix();
glTranslatef(1, 0, 30.0);
glTranslatef(it->position[0], it->position[3] * 3, it->position[2]);
glRotatef(100.f, -5.0, -10, 0);
it->addlight();
it->draw();
it++;
glPopMatrix();
}
}
#include "SpotLight.h"
using namespace std;
GLfloat target[3] = { 0.0f, 0.0f, 0.0f };
GLfloat color[3] = { 1.0f, 1.0f, 1.0f };
GLfloat cutoff(5.0f);
GLfloat exponent(15.0f);
SpotLight::SpotLight(GLfloat x, GLfloat y, GLfloat z, GLfloat w) {
position[0] = x;
position[1] = y;
position[2] = z;
position[3] = w;
direction[0] = target[0] - position[0];
direction[1] = target[1] - position[1];
direction[2] = (target[2] - position[2]);
}
void SpotLight::addlight() {
glEnable(GL_LIGHT1);
glLightfv(GL_LIGHT1, GL_DIFFUSE, color);
glLightfv(GL_LIGHT1, GL_SPECULAR, color);
glLightfv(GL_LIGHT1, GL_POSITION, position);
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, direction);
glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, cutoff);
glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, exponent);
}
void SpotLight::draw() {
if (!glIsEnabled(GL_LIGHT1))
return;
glPushMatrix();
GLfloat up[3] = { 0, 1, 0 };
lookAt(position, target, up);
GLfloat ambient[4] = { 0.8f, 0.8f, 0.8f, 1.0f };
GLfloat diffuse[4] = { 0.01f, 0.01f, 0.01f, 1.0f };
GLfloat specular[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
GLfloat shininess = 32.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
glMaterialf(GL_FRONT, GL_SHININESS, shininess);
glutSolidCone(0.3, 0.6, 10, 10);
glPushMatrix();
glTranslatef(0, 0, 0.1f);
glutSolidCylinder(0.2, 0.39, 10, 10);
glPopMatrix();
glDisable(GL_LIGHTING);
glColor3fv(color);
glutSolidSphere(0.2, 100, 100);
glEnable(GL_LIGHTING);
glPopMatrix();
}
void SpotLight::normalize(const GLfloat* vec, GLfloat* output)
{
GLfloat length = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
output[0] /= length;
output[1] /= length;
output[2] /= length;
}
void SpotLight::cross(const GLfloat* vec1, const GLfloat* vec2, GLfloat * output) {
output[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1];
output[1] = vec1[2] * vec2[0] - vec1[0] * vec2[2];
output[2] = vec1[0] * vec2[1] - vec1[1] * vec2[0];
}
void SpotLight::lookAt(GLfloat* eye, GLfloat* center, GLfloat* up)
{
GLfloat f[3] = { center[0] - eye[0],
center[1] - eye[1],
center[2] - eye[2] };
normalize(f, f);
GLfloat u[3];
normalize(up, u);
GLfloat s[3];
cross(f, u, s);
normalize(s, s);
cross(s, f, u);
normalize(u, u);
}
void drawScene() {
glPushMatrix();
glTranslatef(pointlight.position[0], pointlight.position[1], pointlight.position[2]);
pointlight.addLight();
glPopMatrix();
// Draw road
glPushMatrix();
glScalef(10, 10, 8.5);
glTranslatef(-0.018f, 0, 0.75);
glRotatef(180.f, 0, 1, 0);
models[0]->renderTheModel();
glPopMatrix();
//Draw Car Model
glPushMatrix();
glMultMatrixf(carros[0]->local);
carros[0]->draw();
glPopMatrix();
//Draw spotlights
glPushMatrix();
lamps->Add();
glPopMatrix();
}
You are only ever setting LIGHT1, which means only 1 light is going to have been enabled (the last one). If you specify GL_LIGHT0 + index, you'll be able to enable more.
void SpotLight::addlight(int index) {
glEnable(GL_LIGHT0 + index);
glLightfv(GL_LIGHT0 + index, GL_DIFFUSE, color);
glLightfv(GL_LIGHT0 + index, GL_SPECULAR, color);
glLightfv(GL_LIGHT0 + index, GL_POSITION, position);
glLightfv(GL_LIGHT0 + index, GL_SPOT_DIRECTION, direction);
glLightf(GL_LIGHT0 + index, GL_SPOT_CUTOFF, cutoff);
glLightf(GL_LIGHT0 + index, GL_SPOT_EXPONENT, exponent);
}
And then you simply need to pass the index in when enabling
void CreateLamps::Add() {
std::vector<SpotLight>::iterator it = lamp.begin();
while (it != lamp.end())
{
glPushMatrix();
glTranslatef(1, 0, 30.0);
glTranslatef(it->position[0], it->position[3] * 3, it->position[2]);
glRotatef(100.f, -5.0, -10, 0);
it->addlight(it - lamp.begin());
it->draw();
it++;
glPopMatrix();
}
}
Just be aware that you might run out of lights after 8, so you might want to check the value of GL_MAX_LIGHTS...
int numLights = 0;
glGetIntegerv(GL_MAX_LIGHTS, &numLights);
std::cout << "GL_MAX_LIGHTS " << numLights << std::endl;
Presumably you are drawing the road after drawing all of the lights.
Your code does this:
For each light:
Set light 1 according to that light's parameters
Draw the shape of the light itself
Draw the road.
When the road gets drawn, light 1 is set up with the parameters of the last light that was drawn. So it uses these light parameters to draw the road. You overwrote all the parameters of the other lights already.
If you want to draw the road with all the lights, then all the lights have to be set up when you draw the road. Not just the last one.
Note that you can only set up 8 lights at once in OpenGL. If have more than 8 lights pointing at the road, you will have to split up the road into different sections so that each section has up to 8 lights.
Standard disclaimer in case you aren't aware already: You are using old-style (fixed-function) OpenGL which has been superseded by shader-based OpenGL (version 3 and 4). This API is okay for simple programs but it won't let you use the full flexibility and performance of your graphics card.

(OpenGL ShadowMap)Shadow cast on incorrect faces

Here is the demo image:
(Left top 256x256 rect is the depth texture)
I render the shadow map in the first pass(with parallel projection),
then render the scene in the second pass,
then render the scene with shadow map in the final pass.
the shadow is sometimes rendered twice or on the wrong surfaces
Is there any solution?
Full code here:
typedef struct
{
vec3_t org;//origin
vec3_t off;//position offset
vec3_t ang;//angle
float dist;//radius
int w;//default 512
int h;//default 512
int depth;//depth texture
int color;//color texture
int dimension;//default = 8
float mvmatrix[16];
float projmatrix[16];
cl_entity_t *followent;
int inuse;
}sdlight_t;
void R_RenderDepthMap()
{
qglPolygonOffset( 5.0, 0.0 );
qglEnable(GL_POLYGON_OFFSET_FILL);
if(cursdlight->followent)
{
VectorCopy(cursdlight->followent->origin, cursdlight->org);
}
qglMatrixMode(GL_PROJECTION);
qglLoadIdentity();
qglOrtho(-cursdlight->w / cursdlight->dimension, cursdlight->w / cursdlight->dimension, -cursdlight->h / cursdlight->dimension, cursdlight->h / cursdlight->dimension, -9999, 9999);//cursdlight->dist
qglMatrixMode(GL_MODELVIEW);
qglLoadIdentity();
qglRotatef(-90, 1, 0, 0);
qglRotatef(90, 0, 0, 1);
qglRotatef(-cursdlight->ang[2], 1, 0, 0);
qglRotatef(-cursdlight->ang[0], 0, 1, 0);
qglRotatef(-cursdlight->ang[1], 0, 0, 1);
qglTranslatef(-cursdlight->org[0], -cursdlight->org[1], -cursdlight->org[2]);
qglViewport(0, 0, cursdlight->w, cursdlight->h);
glGetFloatv(GL_PROJECTION_MATRIX, cursdlight->projmatrix);
glGetFloatv(GL_MODELVIEW_MATRIX, cursdlight->mvmatrix);
qglDepthRange(1.0, 0.0);
qglDepthFunc(GL_LEQUAL);
qglEnable(GL_CULL_FACE);
//qglCullFace(GL_FRONT);
qglClearColor(1, 1, 1, 1);
qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Render Models..
R_DrawEntitiesOnList();
qglDisable(GL_POLYGON_OFFSET_FILL);
qglBindFramebufferEXT(GL_READ_FRAMEBUFFER, s_BackBufferFBO.s_hBackBufferFBO);
qglActiveTextureARB(GL_TEXTURE0);
qglBindTexture(GL_TEXTURE_2D, cursdlight->depth);
qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 0, 0, cursdlight->w, cursdlight->h, 0);
}
void R_SetupShadowLight(void)
{
// enable automatic texture coordinates generation
GLfloat planeS[] = {1.0, 0.0, 0.0, 0.0};
GLfloat planeT[] = {0.0, 1.0, 0.0, 0.0};
GLfloat planeR[] = {0.0, 0.0, 1.0, 0.0};
GLfloat planeQ[] = {0.0, 0.0, 0.0, 1.0};
// setup texture stages
qglActiveTextureARB(GL_TEXTURE0_ARB);
qglBindTexture(GL_TEXTURE_2D, cursdlight->depth);
qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_REF_TO_TEXTURE);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
qglTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY);
qglEnable(GL_TEXTURE_GEN_S);
qglEnable(GL_TEXTURE_GEN_T);
qglEnable(GL_TEXTURE_GEN_R);
qglEnable(GL_TEXTURE_GEN_Q);
qglTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
qglTexGenfv(GL_S, GL_EYE_PLANE, planeS);
qglTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
qglTexGenfv(GL_T, GL_EYE_PLANE, planeT);
qglTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
qglTexGenfv(GL_R, GL_EYE_PLANE, planeR);
qglTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
qglTexGenfv(GL_Q, GL_EYE_PLANE, planeQ);
// load texture projection matrix
qglMatrixMode(GL_TEXTURE);
qglLoadIdentity();
qglTranslatef(0.5, 0.5, 0.5);
qglScalef(0.5, 0.5, 0.5);
qglMultMatrixf(cursdlight->projmatrix);
qglMultMatrixf(cursdlight->mvmatrix);
qglMatrixMode(GL_MODELVIEW);
if (gl_polyoffset->value)
{
qglEnable(GL_POLYGON_OFFSET_FILL);
qglPolygonOffset(-1, -gl_polyoffset->value);
}
qglDepthMask(GL_FALSE);
qglEnable(GL_BLEND);
qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
qglColor4f(1,1,1,1);
qglUseProgramObjectARB(shadow_program);
qglUniform1iARB(shadow_uniform.shadowmap, 0);
}
void R_FinishShadowLight(void)
{
qglUseProgramObjectARB(0);
if (gl_polyoffset->value)
{
qglDisable(GL_POLYGON_OFFSET_FILL);
}
qglDepthMask(GL_TRUE);
qglDisable(GL_BLEND);
qglActiveTextureARB(GL_TEXTURE0_ARB);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
qglMatrixMode(GL_TEXTURE);
qglLoadIdentity();
qglDisable(GL_TEXTURE_GEN_S);
qglDisable(GL_TEXTURE_GEN_T);
qglDisable(GL_TEXTURE_GEN_R);
qglDisable(GL_TEXTURE_GEN_Q);
qglMatrixMode(GL_MODELVIEW);
}
void R_DrawShadows(void)
{
for (int i = 0; i < numsdlights; i++)
{
cursdlight = &sdlights[i];
if(!R_ShouldCastShadow())
continue;
R_SetupShadowLight();
R_DrawSceneShadow();
R_FinishShadowLight();
}
}
GLSL part:
//vertex shader
varying vec4 shadowcoord;
void main()
{
shadowcoord = gl_TextureMatrix[0] * gl_Vertex;
gl_Position = ftransform();
}
//fragment shader
#version 120
uniform sampler2DShadow shadowmap;
varying vec4 shadowcoord;
uniform float xoffset = 1.0/512.0;
uniform float yoffset = 1.0/512.0;
float lookup(vec4 coord, vec2 offSet)
{
return shadow2DProj(shadowmap, coord + vec4(offSet.x * xoffset, offSet.y * yoffset, 0.0, 0.0) ).w;
}
void main()
{
float shadow;
shadow = lookup(shadowcoord, vec2(0.0,0.0)) + lookup(shadowcoord, vec2(0.035,0.0)) + lookup(shadowcoord, vec2(-0.035,0.0)) + lookup(shadowcoord, vec2(0.0,0.035)) + lookup(shadowcoord, vec2(0.0,-0.035));
shadow /= 5.0;
if(shadow == 1.0)
discard;
else
gl_FragColor = vec4(0.0, 0.0, 0.0, (1.0-shadow) * 0.5);
}

Drawing the Axis with its arrow using OpenGL in visual studio 2010 and c++

using openGL, I can draw the Axis (x,y and z) in the center of screen, But I can not draw their arrows at the end of each line. in 2d I saw an example code which using GL_LINE_STRIP, but in 3D, I googled alot and I did not find any example;
Now I just put a point at the end of each line; but Actually it is very ugly.
How can I draw it?
this code is just draw the cylinder of z axe in proper position; How can I set others?
void Axis_3D (void)
{
GLUquadric* cyl = gluNewQuadric();
GLUquadric* cy2 = gluNewQuadric();
GLUquadric* cy3 = gluNewQuadric();
// gluCylinder (cyl, 0.02, 0.02, 4, 16, 1); // Body of axis.
glColor3f (1,1,1); // Make arrow head white.
glPushMatrix ();
glTranslatef (0.0,0.0,4);
gluCylinder(cyl, 0.04, 0.000, 0.1, 12,1); // Cone at end of axis.
glTranslatef (0.0,4,0.0);
gluCylinder (cy2, 0.04, 0.001, 0.1, 12,1);
glTranslatef (4,0.0,0.0);
gluCylinder (cy3, 0.04, 0.001, 0.1, 12,1);
glPopMatrix ();
}
thank you so much!
I use those functions to draw arrows in a cutey little simulation program. Have fun using them :)
#define RADPERDEG 0.0174533
void Arrow(GLdouble x1,GLdouble y1,GLdouble z1,GLdouble x2,GLdouble y2,GLdouble z2,GLdouble D)
{
double x=x2-x1;
double y=y2-y1;
double z=z2-z1;
double L=sqrt(x*x+y*y+z*z);
GLUquadricObj *quadObj;
glPushMatrix ();
glTranslated(x1,y1,z1);
if((x!=0.)||(y!=0.)) {
glRotated(atan2(y,x)/RADPERDEG,0.,0.,1.);
glRotated(atan2(sqrt(x*x+y*y),z)/RADPERDEG,0.,1.,0.);
} else if (z<0){
glRotated(180,1.,0.,0.);
}
glTranslatef(0,0,L-4*D);
quadObj = gluNewQuadric ();
gluQuadricDrawStyle (quadObj, GLU_FILL);
gluQuadricNormals (quadObj, GLU_SMOOTH);
gluCylinder(quadObj, 2*D, 0.0, 4*D, 32, 1);
gluDeleteQuadric(quadObj);
quadObj = gluNewQuadric ();
gluQuadricDrawStyle (quadObj, GLU_FILL);
gluQuadricNormals (quadObj, GLU_SMOOTH);
gluDisk(quadObj, 0.0, 2*D, 32, 1);
gluDeleteQuadric(quadObj);
glTranslatef(0,0,-L+4*D);
quadObj = gluNewQuadric ();
gluQuadricDrawStyle (quadObj, GLU_FILL);
gluQuadricNormals (quadObj, GLU_SMOOTH);
gluCylinder(quadObj, D, D, L-4*D, 32, 1);
gluDeleteQuadric(quadObj);
quadObj = gluNewQuadric ();
gluQuadricDrawStyle (quadObj, GLU_FILL);
gluQuadricNormals (quadObj, GLU_SMOOTH);
gluDisk(quadObj, 0.0, D, 32, 1);
gluDeleteQuadric(quadObj);
glPopMatrix ();
}
void drawAxes(GLdouble length)
{
glPushMatrix();
glTranslatef(-length,0,0);
Arrow(0,0,0, 2*length,0,0, 0.2);
glPopMatrix();
glPushMatrix();
glTranslatef(0,-length,0);
Arrow(0,0,0, 0,2*length,0, 0.2);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,-length);
Arrow(0,0,0, 0,0,2*length, 0.2);
glPopMatrix();
}
When I hacked my frustum illustration program I wrote a little arrow drawing helper function draw_arrow, that draws an screen aligned arrow + annotation (uses glutStorkeCharacter).
Note that it uses the fixed function pipeline modelview matrix to determine the local space base vectors. If you want to use it in a shader based pipeline you'll have to pass the modelview matrix into it by an additional parameter.
https://github.com/datenwolf/codesamples/blob/master/samples/OpenGL/frustum/frustum.c
void draw_arrow(
float ax, float ay, float az, /* starting point in local space */
float bx, float by, float bz, /* starting point in local space */
float ah, float bh, /* arrow head size start and end */
char const * const annotation, /* annotation string */
float annot_size /* annotation string height (local units) */ )
{
int i;
GLdouble mv[16];
glGetDoublev(GL_MODELVIEW_MATRIX, mv);
/* We're assuming the modelview RS part is (isotropically scaled)
* orthonormal, so the inverse is the transpose.
* The local view direction vector is the 3rd column of the matrix;
* assuming the view direction to be the normal on the arrows tangent
* space taking the cross product of this with the arrow direction
* yields the binormal to be used as the orthonormal base to the
* arrow direction to be used for drawing the arrowheads */
double d[3] = {
bx - ax,
by - ay,
bz - az
};
normalize_v(d);
double r[3] = { mv[0], mv[4], mv[8] };
int rev = scalarproduct_v(d, r) < 0.;
double n[3] = { mv[2], mv[6], mv[10] };
{
double const s = scalarproduct_v(d,n);
for(int i = 0; i < 3; i++)
n[i] -= d[i]*s;
}
normalize_v(n);
double b[3];
crossproduct_v(n, d, b);
/* Make a 60° arrowhead ( sin(60°) = 0.866... ) */
GLfloat const pos[][3] = {
{ax, ay, az},
{bx, by, bz},
{ ax + (0.866*d[0] + 0.5*b[0])*ah,
ay + (0.866*d[1] + 0.5*b[1])*ah,
az + (0.866*d[2] + 0.5*b[2])*ah },
{ ax + (0.866*d[0] - 0.5*b[0])*ah,
ay + (0.866*d[1] - 0.5*b[1])*ah,
az + (0.866*d[2] - 0.5*b[2])*ah },
{ bx + (-0.866*d[0] + 0.5*b[0])*bh,
by + (-0.866*d[1] + 0.5*b[1])*bh,
bz + (-0.866*d[2] + 0.5*b[2])*bh },
{ bx + (-0.866*d[0] - 0.5*b[0])*bh,
by + (-0.866*d[1] - 0.5*b[1])*bh,
bz + (-0.866*d[2] - 0.5*b[2])*bh }
};
GLushort const idx[][2] = {
{0, 1},
{0, 2}, {0, 3},
{1, 4}, {1, 5}
};
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, pos);
glDrawElements(GL_LINES, 2*5, GL_UNSIGNED_SHORT, idx);
glDisableClientState(GL_VERTEX_ARRAY);
if(annotation) {
float w = 0;
for(char const *c = annotation; *c; c++)
w += glutStrokeWidth(GLUT_STROKE_ROMAN, *c);
w *= annot_size / 100.;
float tx = (ax + bx)/2.;
float ty = (ay + by)/2.;
float tz = (az + bz)/2.;
GLdouble r[16] = {
d[0], d[1], d[2], 0,
b[0], b[1], b[2], 0,
n[0], n[1], n[2], 0,
0, 0, 0, 1
};
glPushMatrix();
glTranslatef(tx, ty, tz);
glMultMatrixd(r);
if(rev)
glScalef(-1, -1, 1);
glTranslatef(-w/2., annot_size*0.1, 0);
draw_strokestring(GLUT_STROKE_ROMAN, annot_size, annotation);
glPopMatrix();
}
}
Help yourself.
I use the help of The Quantum Physicist and merge it with my own code, at last draw this Axes :
#define RADPERDEG 0.0174533
void Arrow(GLdouble x1,GLdouble y1,GLdouble z1,GLdouble x2,GLdouble y2,GLdouble z2,GLdouble D)
{
double x=x2-x1;
double y=y2-y1;
double z=z2-z1;
double L=sqrt(x*x+y*y+z*z);
GLUquadricObj *quadObj;
GLUquadric* cyl = gluNewQuadric();
GLUquadric* cy2 = gluNewQuadric();
GLUquadric* cy3 = gluNewQuadric();
glPushMatrix ();
glTranslated(x1,y1,z1);
if((x!=0.)||(y!=0.)) {
glRotated(atan2(y,x)/RADPERDEG,0.,0.,1.);
glRotated(atan2(sqrt(x*x+y*y),z)/RADPERDEG,0.,1.,0.);
} else if (z<0){
glRotated(180,1.,0.,0.);
}
glTranslatef(0,0,L-4*D);
gluQuadricDrawStyle (cyl, GLU_FILL);
gluQuadricNormals (cyl, GLU_SMOOTH);
glColor3f (1,1,1);
glTranslatef (0.0,0.0,4);
gluCylinder(cyl, 0.04, 0.0, 1.0, 12,1);
gluDeleteQuadric(cyl);
glTranslatef(0,0,-L+4*D);
gluQuadricDrawStyle (cy2, GLU_FILL);
gluQuadricNormals (cy2, GLU_SMOOTH);
glColor3f (1,1,1);
glTranslatef (0.0,4,0.0);
gluCylinder(cy2, 0.4, 0.4,L-1.6, 12,1);
gluDeleteQuadric(cy2);
glPopMatrix();
}
void drawAxes(GLdouble length)
{
glPushMatrix();
glTranslatef(-length,0,0);
Arrow(0,0,0, 2*length,0,0,0.2);
glPopMatrix();
glPushMatrix();
glTranslatef(0,-length,0);
Arrow(0,0,0, 0,2*length,0,0.2);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,-length);
Arrow(0,0,0, 0,0,2*length,0.2);
glPopMatrix();
}

Why is my diffuse/specular lighting not working?

I've got an OpenGL program running, and it displays geometry, but it's all "flat," one gray tone, with no diffuse shading or specular reflection:
Pictured are three tori, each made of quad strips. We should see shading, but we don't. What am I doing wrong?
Here is the code where I set the vertices and normals (draw_torus() is called to build a display list):
/* WrapTorus, adapted from
http://www.math.ucsd.edu/~sbuss/MathCG/OpenGLsoft/WrapTorus/WrapTorus.html
by Sam Buss */
/*
* Issue vertex command for segment number j of wrap number i.
* Normal added by Lars Huttar.
* slices1 = numWraps; slices2 = numPerWrap.
*/
void putVert(float i, float j, float slices1, float slices2, float majR, float minR) {
float wrapFrac = j / slices2;
/* phi is rotation about the circle of revolution */
float phi = PI2 * wrapFrac;
/* theta is rotation about the origin, in the xz plane. */
float theta = PI2 * (i + wrapFrac) / slices1;
float y = minR * (float)sin(phi);
float r = majR + minR * (float)cos(phi);
float x = (float)sin(theta) * r;
float z = (float)cos(theta) * r;
/* normal vector points to (x,y,z) from: */
float xb = (float)sin(theta) * majR;
float zb = (float)cos(theta) * majR;
glNormal3f(x - xb, y, z - zb);
glVertex3f(x, y, z);
}
static void draw_torus(int numPerWrap, int numWraps, float majR, float minR) {
int i, j;
glBegin( GL_QUAD_STRIP );
for (i=0; i < numWraps; i++ ) {
for (j=0; j < numPerWrap; j++) {
putVert((float)i, (float)j, (float)numWraps, (float)numPerWrap, majR, minR);
putVert((float)(i + 1), (float)j, (float)numWraps, (float)numPerWrap, majR, minR);
}
}
putVert(0.0, 0.0, (float)numWraps, (float)numPerWrap, majR, minR);
putVert(1.0, 0.0, (float)numWraps, (float)numPerWrap, majR, minR);
glEnd();
}
Is there something wrong with the order of vertices?
Here is the part of the init function where the display list is built:
GLfloat white[4] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat color[4] = { 0.5, 0.6, 0.7, 1.0 };
...
glShadeModel(GL_SMOOTH);
torusDL = glGenLists (1);
glNewList(torusDL, GL_COMPILE);
setMaterial(color, white, 100);
draw_torus(8, 45, 1.0, 0.05);
glEndList();
where setMaterial() just does:
static void setMaterial(const GLfloat color[3], const GLfloat hlite[3], int shininess) {
glColor3fv(color);
glMaterialfv(GL_FRONT, GL_SPECULAR, hlite);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
glMateriali(GL_FRONT, GL_SHININESS, shininess); /* [0,128] */
}
Here is lighting that's also done during initialization:
GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
GLfloat color[4] = {0.20, 0.20, 0.20, 1.00};
GLfloat spec[4] = {0.30, 0.30, 0.30, 1.00};
GLfloat shiny = 8.0;
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
Here is where the display list gets called, in the draw function:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glLoadIdentity();
glScalef(3.5, 3.5, 3.5);
for (i = 0; i < ac->nrings; i++) {
glScalef(0.8, 0.8, 0.8);
glRotatef(...);
glCallList(torusDL);
}
glFlush();
glPopMatrix();
glXSwapBuffers(dpy, window);
The full .c source file for this "glx hack" is here. In case it makes a difference, this code is in the context of xscreensaver.
As you see, glEnable(GL_NORMALIZE) normalizes the normal vectors after the transformations used for lighting calculations (in fixed-function pipelines). These calculations rely on unit length normals for correct results.
It's worth pointing out that the transforms applied to normal vectors are not the same as the transforms applied to vertex geometry. The OpenGL 2.1 specification describes the transform, as do many other resources. As a vector, a normal has the homogeneous representation: [nx, ny, nz, 0] - a point at 'infinity', and a mathematically elegant way to unify matrix and 4-vector operations in the GL pipeline.
Of course, you could perform this normalization yourself, and it may be more efficient to do so, as your torus geometry is only generated once for a pre-compiled display list:
nx = x - b, ny = y, nz = z - zb;
nl = 1.0f / sqrtf(nx * nx + ny * ny + nz * nz);
glNormal3f(nx * nl, ny * nl, nz * nl);
Be sure to check (nl) for division by zero (or some epsilon), if that's a possibility.

OpenGL evaluator only partially lit

I was trying to make a small wave generator in OpenGL with C++, using an evaluator.
However, I haven't had much luck since my evaluator only gets partially lit.
Why does this happen?
Below I include full source code for completeness' sake, you'll probably only have to look at init(), display() and the constants at the top of the file.
#include <gl/glui.h>
#include <math.h>
const int DIMX = 500;
const int DIMY = 500;
const int INITIALPOS_X = 200;
const int INITIALPOS_Y = 200;
// Aspect ratio (calculated on the fly)
float xy_aspect;
// UI aux. matrices
float view_rotate[16] = { 1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1 };
float obj_pos[] = { 0.0, 0.0, 0.0 };
float obj_pan[] = { 0.0, 0.0, 0.0 };
// Referential axis
double axis_radius_begin = 0.2;
double axis_radius_end = 0.0;
double axis_lenght = 16.0;
int axis_nslices = 8;
int axis_nstacks = 1;
// Light 0 properties
float light0_position[] = {5.0, 5.0, 5.0, 0.0};
float light0_ambient[] = {0.0, 0.0, 0.0, 1.0};
float light0_diffuse[] = {0.6, 0.6, 0.6, 1.0};
float light0_specular[] = {1.0, 1.0, 1.0, 1.0};
float light0_kc = 0.0;
float light0_kl = 1.0;
float light0_kq = 0.0;
double light0x = 5.0;
double light0y = 5.0;
double light0z = 5.0;
double symb_light0_radius = 0.2;
int symb_light0_slices = 8;
int symb_light0_stacks =8;
// Ambient light source properties
float light_ambient[] = {0.5, 0.5, 0.5, 1.0}; /* Set the background ambient lighting. */
// Windowing related variables
int main_window;
GLUquadric* glQ;
GLUI *glui;
const unsigned int gridSize = 40;
float grid[gridSize][gridSize][3];
const int uSize = gridSize;
const int vSize = gridSize;
GLfloat ambient[] = {0.2, 0.2, 0.2, 1.0};
GLfloat position[] = {0.0, 0.0, 2.0, 1.0};
GLfloat mat_diffuse[] = {0.6, 0.6, 0.6, 1.0};
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
float mat_shininess[] = {50.0};
void display(void) {
static float value = 0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glFrustum( -xy_aspect*.04, xy_aspect*.04, -.04, .04, .1, 50.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( obj_pos[0], obj_pos[1], -obj_pos[2]-25 );
glTranslatef( obj_pan[0], obj_pan[1], obj_pan[2] );
glRotated( 20.0, 1.0,0.0,0.0 );
glRotated(-45.0, 0.0,1.0,0.0 );
glMultMatrixf( view_rotate );
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glColor3f(1.0,0.0,0.0);
glPushMatrix();
glRotated(90.0, 0.0,1.0,0.0 );
gluCylinder(glQ, axis_radius_begin, axis_radius_end,
axis_lenght, axis_nslices, axis_nstacks);
glPopMatrix();
glColor3f(0.0,1.0,0.0);
glPushMatrix();
glRotated(-90.0, 1.0,0.0,0.0 );
gluCylinder(glQ, axis_radius_begin, axis_radius_end,
axis_lenght, axis_nslices, axis_nstacks);
glPopMatrix();
glColor3f(0.0,0.0,1.0);
glPushMatrix();
gluCylinder(glQ, axis_radius_begin, axis_radius_end,
axis_lenght, axis_nslices, axis_nstacks);
glPopMatrix();
light0_position[0] = light0x;
light0_position[1] = light0y;
light0_position[2] = light0z;
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
glColor3f(1.0,1.0,0.0);
gluQuadricOrientation( glQ, GLU_INSIDE);
glPushMatrix();
glTranslated(light0x,light0y,light0z);
gluSphere(glQ, symb_light0_radius, symb_light0_slices, symb_light0_stacks);
glPopMatrix();
gluQuadricOrientation( glQ, GLU_OUTSIDE);
gluQuadricDrawStyle(glQ, GLU_FILL);
gluQuadricNormals(glQ, GLU_SMOOTH);
gluQuadricOrientation(glQ, GLU_OUTSIDE);
gluQuadricTexture(glQ, GL_FALSE);
for (unsigned int y = 0; y < vSize; ++y) {
for (unsigned int x = 0; x < uSize; ++x) {
float xVal = 5*3.14/gridSize*x;
float yVal = 5*3.14/gridSize*y;
grid[y][x][0] = (float) x/gridSize*10.0;
grid[y][x][1] = sin(xVal + value) + sin(yVal + value);
grid[y][x][2] = (float) y/gridSize*10.0;
}
}
glMap2f(GL_MAP2_VERTEX_3, 0, 1 , 3, uSize, 0, 1, uSize * 3, vSize, &grid[0][0][0]);
glEvalMesh2(GL_FILL, 0, gridSize, 0, gridSize);
value += 3.14/25;
if (value > 3.14*2)
value = 0;
// swapping the buffers causes the rendering above to be shown
glutSwapBuffers();
glFlush();
}
/* Mouse handling */
void processMouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
}
if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
{
}
glutPostRedisplay();
}
void processMouseMoved(int x, int y)
{
// pedido de refrescamento da janela
glutPostRedisplay();
}
void processPassiveMouseMoved(int x, int y)
{
// pedido de refrescamento da janela
glutPostRedisplay();
}
void reshape(int w, int h)
{
int tx, ty, tw, th;
GLUI_Master.get_viewport_area( &tx, &ty, &tw, &th );
glViewport( tx, ty, tw, th );
xy_aspect = (float)tw / (float)th;
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27: // tecla de escape termina o programa
exit(0);
break;
}
}
void glut_idle( void )
{
if ( glutGetWindow() != main_window )
glutSetWindow(main_window);
glutPostRedisplay();
}
void init()
{
glQ = gluNewQuadric();
glFrontFace(GL_CCW); // Front faces defined using a counterclockwise rotation
glDepthFunc(GL_LEQUAL); // Por defeito e GL_LESS
glEnable(GL_DEPTH_TEST); // Use a depth (z) buffer to draw only visible objects
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
// Face Culling para aumentar a velocidade
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); // GL_FRONT, GL_BACK, GL_FRONT_AND_BACK
// Define que modelo de iluminacao utilizar; consultar o manual de referencia
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light_ambient); // define luz ambiente
glLightModelf (GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
glLightModeli (GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
// por defeito a cor de fundo e o preto
// glClearColor(1.0,1.0,1.0,1.0); // cor de fundo a branco
// declaracoes para a fonte luz GL_LIGHT0
glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, light0_kc);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, light0_kl);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, light0_kq);
// NOTA: a direccao e a posicao de GL_LIGHT0 estao na rotina display(), pelo
// que as isntrucoes seguntes nao sao necessarias
//glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 90.0);
//glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);
//glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_NORMALIZE);
glEnable(GL_MAP2_VERTEX_3);
glMapGrid2f(gridSize, 0.0, 1.0, gridSize, 0.0, 1.0);
glShadeModel(GL_SMOOTH);
glPolygonMode(GL_FRONT, GL_FILL);
//glPolygonMode(GL_FRONT, GL_LINE);
}
void do_nothing(int key, int x, int y) {}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize (DIMX, DIMY);
glutInitWindowPosition (INITIALPOS_X, INITIALPOS_Y);
main_window = glutCreateWindow (argv[0]);
glutDisplayFunc(display);
GLUI_Master.set_glutReshapeFunc(reshape);
GLUI_Master.set_glutKeyboardFunc (keyboard);
GLUI_Master.set_glutMouseFunc(processMouse);
glutMotionFunc(processMouseMoved);
glutPassiveMotionFunc(processPassiveMouseMoved);
GLUI_Master.set_glutSpecialFunc( do_nothing );
/*** Create the bottom subwindow ***/
glui = GLUI_Master.create_glui_subwindow( main_window, GLUI_SUBWINDOW_BOTTOM );
glui->set_main_gfx_window( main_window );
GLUI_Rotation *view_rot = glui->add_rotation( "Rotation", view_rotate );
view_rot->set_spin( 1.0 );
glui->add_column( false );
GLUI_Translation *trans_z = glui->add_translation( "Zoom", GLUI_TRANSLATION_Z, &obj_pos[2] );
trans_z->set_speed( .1 );
glui->add_column(false);
GLUI_Translation *trans_pan = glui->add_translation("Pan", GLUI_TRANSLATION_XY, &obj_pan[0]);
trans_pan->set_speed(.1);
GLUI_Master.set_glutIdleFunc( glut_idle );
init();
glutMainLoop();
return 0;
}
You say OpenGL evaluators don't need normals to set. This is only partly true. You only don't need to set normals if you enable automatically generated normals for evaluators by calling:
glEnable(GL_AUTO_NORMAL);
Just enabling GL_NORMALIZE won't do it.
But you can of course also specify your own normals by providing control points for GL_MAP2_NORMAL in the same way like for GL_MAP2_VERTEX_3.
And the answer won't be complete without mentioning that OpenGL evaluators are highly deprecated and most probably implemented in softare by the driver. So just rolling your own Bezier evaluation code (which is not very hard) and generating a simple mesh grid drawn as GL_TRIANGLES will surely be a better idea.