Opengl smooth shading generated object - c++

I generated a 3d shape using a perlin noise.
I am trying to smooth shade it, to do so I calculated the face normal for each of my triangles and then I calculated the normal of each triangle vertex by averaging the normals of the faces they belong to and normalizing the final result.
The final result looks a lot like flat shading (see attached screenshots)
The normals looks correct to me.
I cannot use shaders and have to use the old deprecated way of rendering.
The shape generator :
void Island::generateTopTriangles() {
float xStep = 2 * _xmax / _tess;
float zStep = 2 * _zmax / _tess;
PointMap top;
for (int i = 0; i <= _tess; i++) {
float z = -_zmax + i * zStep;
std::vector<Vector3f> rowTop;
for (int j = 0; j <= _tess; j++) {
float x = -_xmax + j * xStep;
rowTop.emplace_back(x, islandPerlin(x, z), z);
}
top.emplace_back(rowTop);
}
for (int i = 0; i < top.size() - 1; i++) {
const std::vector<Vector3f> &pointRow = top[i];
const std::vector<Vector3f> &pointUpRow = top[i + 1];
std::vector<Triangle> newRow;
for (int j = 0; j < pointRow.size() - 1; j++) {
const Vector3f &p1 = pointRow.at(j);
const Vector3f &p2 = pointRow.at(j + 1);
const Vector3f &p3 = pointUpRow.at(j);
const Vector3f &p4 = pointUpRow.at(j + 1);
Vertex::Ptr v1, v2, v3, v4, v5;
if (j == 0) {
v1 = std::make_shared<Vertex>(Vertex(p1, p3, Vector3f()));
} else { //Retrieve existing Vertex
v1 = newRow[newRow.size() - 1].v2;
}
v2 = std::make_shared<Vertex>(Vertex(p3, p2, Vector3f()));
if (i == 0) {
v3 = std::make_shared<Vertex>(Vertex(p2, p1, Vector3f()));
} else { //Retrieve existing Vertex
v3 = _triangles[_triangles.size() - 1][j == 0 ? 1 : newRow.size() + 1].v3;
}
v4 = std::make_shared<Vertex>(Vertex(p2, p4, Vector3f()));
v5 = std::make_shared<Vertex>(Vertex(p4, p3, Vector3f()));
//Create triangles
newRow.emplace_back(v1, v2, v3, computeNormal(v1->p, v2->p, v3->p));
newRow.emplace_back(v2, v4, v5, computeNormal(v2->p, v4->p, v5->p).invert());
}
_triangles.emplace_back(newRow);
}
}
I compute face normals with a simple cross product between two vectors :
Vector3f Island::computeNormal(const Vector3f &p1, const Vector3f &p2, const Vector3f &p3) {
Vector3f u = {p2.x - p1.x,
p2.y - p1.y,
p2.z - p1.z};
Vector3f v = {p3.x - p1.x,
p3.y - p1.y,
p3.z - p1.z};
Vector3f n = {u.y * v.z - u.z * v.y,
u.z * v.x - u.x * v.z,
u.x * v.y - u.y * v.x};
return n.normalize();
}
The per vertex normals (initialized to 0) :
void Island::computePerVertexNormal() {
for (auto row : _triangles) {
for (auto t : row) {
t.v1->n.x += t.n.x;
t.v1->n.y += t.n.y;
t.v1->n.z += t.n.z;
t.v2->n.x += t.n.x;
t.v2->n.y += t.n.y;
t.v2->n.z += t.n.z;
t.v3->n.x += t.n.x;
t.v3->n.y += t.n.y;
t.v3->n.z += t.n.z;
}
}
for (auto row : _triangles) {
for (auto t : row) {
t.v1->n.normalize();
t.v2->n.normalize();
t.v3->n.normalize();
}
}
}
And finally the drawing part :
void Island::draw() const {
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_BLEND);
glEnable(GL_COLOR_MATERIAL);
GLfloat specular[] = {0.1f, 0.1f, 0.1f, 0.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
GLfloat diffuse[] = {0.5f, 0.5f, 0.5f, 1.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
GLfloat emission[] = {0.0f, 0.0f, 0.0f, 1.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission);
GLfloat shininess = 128.0f;
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
glShadeModel(GL_SMOOTH);
glColor4f(1.0f, 0.5f, 0.0f, 1.0f);
glBegin(GL_TRIANGLES);
for (auto &row : _triangles) {
for (auto &t : row) {
glNormal3f(t.v1->n.x, t.v1->n.y, t.v1->n.z);
glVertex3f(t.v1->p.x, t.v1->p.y, t.v1->p.z);
glNormal3f(t.v2->n.x, t.v2->n.y, t.v2->n.z);
glVertex3f(t.v2->p.x, t.v2->p.y, t.v2->p.z);
glNormal3f(t.v3->n.x, t.v3->n.y, t.v3->n.z);
glVertex3f(t.v3->p.x, t.v3->p.y, t.v3->p.z);
}
}
glEnd();
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_BLEND);
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
}

The solution was simple. I got confused and thought my edges were the vertices. Once corrected, since the new vertices share the correct points this time, the new normals are now calculated from the 6 adjacent triangle faces and are now correct. the generation code is now much simpler as well.
void Island::generateTopTriangles() {
float xStep = 2 * _xmax / _tess;
float zStep = 2 * _zmax / _tess;
float z;
for (int i = 0; i <= _tess; i++) {
z = -_zmax + i * zStep;
std::vector<Vertex::Ptr> row;
for (int j = 0; j <= _tess; j++) {
float x = -_xmax + j * xStep;
row.emplace_back(std::make_shared<Vertex>(Vector3f(x, islandPerlin(x, z), z)));
}
_vertices.emplace_back(row);
}
for (int i = 0; i < _vertices.size() - 1; i++) {
const std::vector<Vertex::Ptr> &pointRow = _vertices[i];
const std::vector<Vertex::Ptr> &pointUpRow = _vertices[i + 1];
std::vector<Triangle::Ptr> newRow;
for (int j = 0; j < pointRow.size() - 1; j++) {
const Vertex::Ptr p1 = pointRow.at(j);
const Vertex::Ptr p2 = pointRow.at(j + 1);
const Vertex::Ptr p3 = pointUpRow.at(j);
const Vertex::Ptr p4 = pointUpRow.at(j + 1);
newRow.emplace_back(std::make_shared<Triangle>(p1, p2, p3, computeNormal(p3->p, p2->p, p1->p)));
newRow.emplace_back(std::make_shared<Triangle>(p3, p2, p4, computeNormal(p4->p, p2->p, p3->p)));
}
_triangles.emplace_back(newRow);
}
}
The rest of the code was actually correct. Here are the results :

Related

Reflection model does not work correctly in OpenGl using glColor3f

I have a depth map as 2D double array 480x640. I visualize it using openGL using glBegin(GL_TRIANGLES).
This is my code, it works correctly:
int main(int argc, char** argv){
ifstream ifs("D:\\DepthMaps1-20\\DepthMap_1.dat", std::ios::binary);
if (ifs) {
double dheight, dwidth;
ifs.read(reinterpret_cast<char*>(&dheight), sizeof dheight);
ifs.read(reinterpret_cast<char*>(&dwidth), sizeof dwidth);
height = static_cast<size_t>(dheight);
width = static_cast<size_t>(dwidth);
vector<vector<double>> dmap(height, vector<double>(width));
for (auto& row : dmap) {
for (double& col : row)
ifs.read(reinterpret_cast<char*>(&col), sizeof col);
}
double fx = 525.0;
double fy = 525.0; // default focal length
double cx = 319.5;
double cy = 239.5; // default optical center
vector<vector<double>> x(height, vector<double>(width));
vector<vector<double>> y(height, vector<double>(width));
vector<vector<double>> z(height, vector<double>(width));
for (unsigned i = 0; i < dmap.size(); i++)
{
for (unsigned j = 0; j < dmap[i].size(); j++)
{
z[i][j] = dmap[i][j] / 500.0;
x[i][j] = (j - cx) * z[i][j] / fx;
y[i][j] = (i - cy) * z[i][j] / fy;
}
}
GLFWwindow * window;
if (!glfwInit())
return -1;
window = glfwCreateWindow(640, 640, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, keyCallback);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glBegin(GL_TRIANGLES);
glColor3f(189.0/255.0, 140.0 / 255.0, 194.0 / 255.0);
for (unsigned i = 0; i < dmap.size(); i++)
{
for (unsigned j = 0; j < dmap[i].size(); j++)
{
if (j < dmap[i].size() - 2 && i < dmap.size() - 2)
{
if (z[i][j] != 0 && z[i][j + 1] != 0 && z[i + 1][j] != 0 && z[i + 1][j+1] != 0)
{
glVertex3d(x[i][j], y[i][j], z[i][j]);
glVertex3d(x[i][j + 1], y[i][j + 1], z[i][j + 1]);
glVertex3d(x[i + 1][j], y[i + 1][j], z[i + 1][j]);
glVertex3d(x[i][j+1], y[i][j+1], z[i][j+1]);
glVertex3d(x[i + 1][j + 1], y[i + 1][j + 1], z[i + 1][j + 1]);
glVertex3d(x[i + 1][j], y[i + 1][j], z[i + 1][j]);
}
}
}
}
glEnd();
glFlush();
glfwSwapBuffers(window);
glfwPollEvents();
}
ifs.close();
}
return 0;}
So now I need to add lighting using mathematical formulas for reflection model. Idea is - lighting is taken as parallel (unidirectional) beams of the same intensity, the size of the light source is not limited. Illumination is set by the direction L [Lx Ly Lz].
This is my code for Lambert reflection model and it works, but I want better result.
float coord = -1.0f;
float coord1 = -1.0f;
float coord2 = -0.0f;
float coord4 = -1.0f;
float coord5 = -2.0f;
float coord6 = -1.0f;
int main(int argc, char** argv)
{
ifstream ifs("D:\\DepthMaps1-20\\DepthMap_1.dat", std::ios::binary);
if (ifs) {
double dheight, dwidth;
ifs.read(reinterpret_cast<char*>(&dheight), sizeof dheight);
ifs.read(reinterpret_cast<char*>(&dwidth), sizeof dwidth);
height = static_cast<size_t>(dheight);
width = static_cast<size_t>(dwidth);
vector<vector<double>> dmap(height, vector<double>(width));
for (auto& row : dmap) {
for (double& col : row)
ifs.read(reinterpret_cast<char*>(&col), sizeof col);
}
double fx = 525.0;
double fy = 525.0; // default focal length
double cx = 319.5;
double cy = 239.5; // default optical center
vector<vector<double>> x(height, vector<double>(width));
vector<vector<double>> y(height, vector<double>(width));
vector<vector<double>> z(height, vector<double>(width));
vector<vector<int>> brightness(height, vector<int>(width));
for (unsigned i = 0; i < dmap.size(); i++)
{
for (unsigned j = 0; j < dmap[i].size(); j++)
{
z[i][j] = dmap[i][j] / 500.0;
x[i][j] = (j - cx) * z[i][j] / fx;
y[i][j] = (i - cy) * z[i][j] / fy;
}
}
GLFWwindow * window;
if (!glfwInit())
return -1;
window = glfwCreateWindow(640, 640, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, keyCallback);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glRotatef(tippangle, 1, 0, 0);
glRotatef(viewangle, 0, 1, 0);
glScalef(scaleF, scaleF, scaleF);
//x
glRasterPos3f(1.1, 0.0, 0.0);
//y
glRasterPos3f(0.0, 1.1, 0.0);
//z
glRasterPos3f(0.0, 0.0, 1.1);
glTranslatef(d[0], d[1], d[2]);
glBegin(GL_TRIANGLES);
for (unsigned i = 0; i < dmap.size(); i++)
{
for (unsigned j = 0; j < dmap[i].size(); j++)
{
if (j < dmap[i].size() - 2 && i < dmap.size() - 2)
{
if (z[i][j] != 0 && z[i][j + 1] != 0 && z[i + 1][j] != 0 && z[i + 1][j + 1] != 0)
{
//Determination of the normal
glm::vec3 left = glm::vec3(0, 1, (z[i][j + 1] - z[i][j+1]));
glm::vec3 right = glm::vec3(1, 0, (z[i + 1][j] - z[i + 1][j]));
glm::vec3 normal = glm::normalize(glm::cross(left, right));
glm::vec3 Position_Light = glm::vec3(coord + 0, coord1+ 0, coord2 + 0); //Light source
glm::vec3 Position_View = glm::vec3(coord4, coord5, coord6); //observer
glm::vec3 Position_Point = glm::vec3(x[i][j], y[i][j], z[i][j]);
//Directions
glm::vec3 Light_Direction = glm::normalize(Position_Light - Position_Point); //To source
glm::vec3 View_Direction = glm::normalize(Position_View - Position_Point); // To the observer
glm::vec3 HalfWay_Direction = glm::normalize(Light_Direction + View_Direction); //Median vector (halfway)
double kd = 1;//diffuse reflectance for the Lambert model
double I = 0; //variable brightness
I = kd * glm::dot(Light_Direction, normal);
glColor3f(I, I, I);
glVertex3d(x[i][j], y[i][j], z[i][j]);
glVertex3d(x[i][j + 1], y[i][j + 1], z[i][j + 1]);
glVertex3d(x[i + 1][j], y[i + 1][j], z[i + 1][j]);
glVertex3d(x[i][j+1], y[i][j+1], z[i][j+1]);
glVertex3d(x[i + 1][j + 1], y[i + 1][j + 1], z[i + 1][j + 1]);
glVertex3d(x[i + 1][j], y[i + 1][j], z[i + 1][j]);
}
}
}
}
glEnd();
glFlush();
glfwSwapBuffers(window);
glfwPollEvents();
}
ifs.close();
}
return 0;
}
This is my result.
And I want this result.
Second result is an example for this work, but using c#. Sourse code here:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
double[,] depth;
int[,] brightness;
bool glConrerolIsLoaded;
float coord = 501.5f;
float coord1 = -17.5f;
float coord2 = -2979.5f;
float coord4 = -73.0f;
float coord5 = 1269.0f;
float coord6 = 413.5f;
int resNum = 0;
private void glControl1_Load(object sender, EventArgs e)
{
glConrerolIsLoaded = true;
GL.ClearColor(Color.Black);
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
glControl1.Invalidate();
panel1.Invalidate();
}
private void numericUpDown2_ValueChanged(object sender, EventArgs e)
{
glControl1.Invalidate();
panel1.Invalidate();
}
private void numericUpDown3_ValueChanged(object sender, EventArgs e)
{
glControl1.Invalidate();
panel1.Invalidate();
}
private void glControl1_Paint(object sender, PaintEventArgs e)
{
GL.LoadIdentity();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
Matrix4 perspectiveMatrix = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(45), glControl1.Width / glControl1.Height, 1.0f, 100.0f);
GL.LoadMatrix(ref perspectiveMatrix);
GL.MatrixMode(MatrixMode.Modelview);
GL.Translate(-25.0, -9.0, -45.0);
GL.Scale(0.04, 0.04, 0.04);
GL.Begin(BeginMode.Points);
for (int i = 0; i < depth.GetLength(0) - 1 ; i++)
{
for (int j = 0; j < depth.GetLength(1) - 1 ; j++)
{
if (depth[i, j] != 0 && depth[i + 1, j] != 0 && /*depth[i + 1, j + 1] != 0 &&*/ depth[i, j + 1] != 0)
{
Vector3 left = new Vector3(0, 1, Convert.ToSingle(depth[i, j + 1]) - Convert.ToSingle(depth[i, j]));
Vector3 right = new Vector3(1, 0, Convert.ToSingle(depth[i + 1, j]) - Convert.ToSingle(depth[i, j]));
Vector3 Normal = Vector3.Normalize(Vector3.Cross(left, right));
Vector3 Position_Light = new Vector3(coord + Convert.ToSingle(numericUpDown1.Value), coord1
+ Convert.ToSingle(numericUpDown2.Value), coord2 + Convert.ToSingle(numericUpDown3.Value));
Vector3 Position_View = new Vector3(coord4, coord5, coord6);
Vector3 Position_Point = new Vector3(i, j, Convert.ToSingle(depth[i, j]));
Vector3 Light_Direction = Vector3.Normalize(Position_Light - Position_Point);
Vector3 View_Direction = Vector3.Normalize(Position_View - Position_Point);
Vector3 HalfWay_Direction = Vector3.Normalize(Light_Direction + View_Direction);
double kd = 1;
double I = 0;
I = kd * Vector3.Dot(Light_Direction, Normal);
GL.Color3(I, I, I);
GL.Vertex3(i, j, depth[i, j]);
}
}
GL.End();
glControl1.SwapBuffers();
}
private void Form1_Load(object sender, EventArgs e)//Считывание карты глубины
{
string path = #"DepthMap_1.dat";
BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open));
double Height1 = reader.ReadDouble();
double Width1 = reader.ReadDouble();
depth = new double[Convert.ToInt16(Height1), Convert.ToInt16(Width1)];
brightness = new int[Convert.ToInt16(Height1), Convert.ToInt16(Width1)];
for (int i = 0; i < depth.GetLength(0); i++)
{
for (int j = 0; j < depth.GetLength(1); j++)
{
depth[i, j] = reader.ReadDouble();
}
}
reader.BaseStream.Close();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
}
}
}
So my question is what is incorrect in my code? If it light position could you please help me to fix it.
Color is composed of ambient + diffuse + specular. The specular component will add reflection which I think is what you are expecting. The calculations you are doing for color
I_diffuse = kd * glm::dot(Light_Direction, normal);
is just diffuse component.
So what you need will be
I_total = I_diffuse + I_specular;
You already have I_diffuse and you can get I_specular as below,
vec3 viewDir = normalize(viewPos - pointPos);
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 I_specular = specularStrength * spec * Ks;
specularStrength can be used to control the strength of reflection.
Note :
The code you are using looks like in a fixed-function pipeline which is way deprecated. If possible please move it to the programmable pipeline with shaders.
You can follow: https://learnopengl.com/Lighting/Basic-Lighting
I found answer, I made mistake here:
//Determination of the normal
glm::vec3 left = glm::vec3(0, 1, (z[i][j + 1] - z[i][j+1]));
glm::vec3 right = glm::vec3(1, 0, (z[i + 1][j] - z[i + 1][j]));
It should be like this:
//Determination of the normal
glm::vec3 left = glm::vec3(0, 1, (z[i][j + 1] - z[i][j]));
glm::vec3 right = glm::vec3(1, 0, (z[i + 1][j] - z[i][j]));

OpenGL: Updating shading with several movable lights

In this code, I'm trying to shade a surface properly based on the position of a light which can be moved. So when you move the light, the surface updates. In addition, I'm looking to have two lights of different colors both shading the same surface. Unfortunately, the surface color remains static.
What I'd like:
1) Have the surface update when the light is moved, and a vector that will use both colors(I'm not 100% on how to do this).
2) Have the lights and normals remain a static color regardless of shading/light.
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#ifdef MAC
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
//Camera variables
int xangle = -270;
int yangle = 0;
//Control Modes (Rotate mode by default)
int mode = 0;
int lightmode = 0;
//Player Position (Y offset so it would not be straddling the grid)
float cubeX = 0;
float cubeY = 0.5;
float cubeZ = 0;
//Vertex arrays for surface
float surfaceX [12][12];
float surfaceY [12][12];
float surfaceZ [12][12];
//Surface Normal arrays
float Nx[11][11];
float Ny[11][11];
float Nz[11][11];
//Color arrays
float R[11][11];
float G[11][11];
float B[11][11];
//Material properties
float Ka = 0.2;
float Kd = 0.4;
float Ks = 0.4;
float Kp = 0.5;
//Light position and color variables
float Light1x = 0;
float Light1y = 5;
float Light1z = 0;
float Light1r = 1;
float Light1g = 0;
float Light1b = 0;
float Light2x = -5;
float Light2y = 5;
float Light2z = -5;
float Light2r = 0;
float Light2g = 1;
float Light2b = 0;
//Random number generator
float RandomNumber(float Min, float Max)
{
return ((float(rand()) / float(RAND_MAX)) * (Max - Min)) + Min;
}
//---------------------------------------
// Initialize material properties
//---------------------------------------
void init_material(float Ka, float Kd, float Ks, float Kp,
float Mr, float Mg, float Mb)
{
// Material variables
float ambient[] = { Ka * Mr, Ka * Mg, Ka * Mb, 1.0 };
float diffuse[] = { Kd * Mr, Kd * Mg, Kd * Mb, 1.0 };
float specular[] = { Ks * Mr, Ks * Mg, Ks * Mb, 1.0 };
// Initialize material
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, Kp);
}
//---------------------------------------
// Initialize light source
//---------------------------------------
void init_light(int light_source, float Lx, float Ly, float Lz,
float Lr, float Lg, float Lb)
{
// Light variables
float light_position[] = { Lx, Ly, Lz, 0.0 };
float light_color[] = { Lr, Lg, Lb, 1.0 };
// Initialize light source
glEnable(GL_LIGHTING);
glEnable(light_source);
glLightfv(light_source, GL_POSITION, light_position);
glLightfv(light_source, GL_AMBIENT, light_color);
glLightfv(light_source, GL_DIFFUSE, light_color);
glLightfv(light_source, GL_SPECULAR, light_color);
glLightf(light_source, GL_CONSTANT_ATTENUATION, 1.0);
glLightf(light_source, GL_LINEAR_ATTENUATION, 0.0);
glLightf(light_source, GL_QUADRATIC_ATTENUATION, 0.0);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
}
//---------------------------------------
// Initialize surface
//---------------------------------------
void init_surface()
{
//Initialize X, select column
for (int i = 0; i < 12; i++)
{
//Select row
//Surface is +1 so the far right normal will be generated correctly
for (int j = 0; j < 12; j++)
{
//-5 to compensate for negative coordinate values
surfaceX[i][j] = i-5;
//Generate random surface height
surfaceY[i][j] = RandomNumber(5, 7) - 5;
//surfaceY[i][j] = 0;
surfaceZ[i][j] = j-5;
}
}
}
void define_normals()
{
//Define surface normals
for (int i = 0; i < 11; i++)
{
for (int j = 0; j < 11; j++)
{
//Get two tangent vectors
float Ix = surfaceX[i+1][j] - surfaceX[i][j];
float Iy = surfaceY[i+1][j] - surfaceY[i][j];
float Iz = surfaceZ[i+1][j] - surfaceZ[i][j];
float Jx = surfaceX[i][j+1] - surfaceX[i][j];
float Jy = surfaceY[i][j+1] - surfaceY[i][j];
float Jz = surfaceZ[i][j+1] - surfaceZ[i][j];
//Do cross product, inverted for upward normals
Nx[i][j] = - Iy * Jz + Iz * Jy;
Ny[i][j] = - Iz * Jx + Ix * Jz;
Nz[i][j] = - Ix * Jy + Iy * Jx;
//Original vectors
//Nx[i][j] = Iy * Jz - Iz * Jy;
//Ny[i][j] = Iz * Jx - Ix * Jz;
//Nz[i][j] = Ix * Jy - Iy * Jx;
float length = sqrt(
Nx[i][j] * Nx[i][j] +
Ny[i][j] * Ny[i][j] +
Nz[i][j] * Nz[i][j]);
if (length > 0)
{
Nx[i][j] /= length;
Ny[j][j] /= length;
Nz[i][j] /= length;
}
}
}
}
void calc_color()
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
//Calculate light vector
//Light position, hardcoded for now 0,1,1
float Lx = Light1x - surfaceX[i][j];
float Ly = Light1y - surfaceY[i][j];
float Lz = Light1z - surfaceZ[i][j];
//std::cout << "Lx: " << Lx << std::endl;
//std::cout << "Ly: " << Ly << std::endl;
//std::cout << "Lz: " << Lz << std::endl;
//Grab surface normals
//These are Nx,Ny,Nz due to compiler issues
float Na = Nx[i][j];
float Nb = Ny[i][j];
float Nc = Nz[i][j];
//std::cout << "Na: " << Na << std::endl;
//std::cout << "Nb: " << Nb << std::endl;
//std::cout << "Nc: " << Nc << std::endl;
//Do cross product
float Color = (Na * Lx) + (Nb * Ly) + (Nc * Lz);
//std::cout << "Color: " << Color << std::endl;
R[i][j] = Color;
G[i][j] = Color;
B[i][j] = Color;
}
}
}
//---------------------------------------
// Init function for OpenGL
//---------------------------------------
void init()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//Viewing Window Modified
glOrtho(-7.0, 7.0, -7.0, 7.0, -7.0, 7.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//Rotates camera
//glRotatef(30.0, 1.0, 1.0, 1.0);
glEnable(GL_DEPTH_TEST);
//Project 3 code
init_surface();
define_normals();
//Shading code
glShadeModel(GL_SMOOTH);
glEnable(GL_NORMALIZE);
//X,Y,Z - R,G,B
init_light(GL_LIGHT1, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
//init_light(GL_LIGHT2, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
//init_light(GL_LIGHT2, 0, 1, 0, 0.5, 0.5, 0.5);
}
void keyboard(unsigned char key, int x, int y)
{
///TODO: allow user to change color of light
//Controls
//Toggle Mode
if (key == 'q')
{
if(mode == 0)
{
mode = 1;
std::cout << "Switched to Light mode (" << mode << ")" << std::endl;
}
else if(mode == 1)
{
mode = 0;
std::cout << "Switched to Rotate mode (" << mode << ")" << std::endl;
}
}
//Toggle light control
else if (key == 'e')
{
if(lightmode == 0)
{
lightmode = 1;
std::cout << "Switched to controlling light 2 (" << lightmode << ")" << std::endl;
}
else if(lightmode == 1)
{
lightmode = 0;
std::cout << "Switched to controlling light 1 (" << lightmode << ")" << std::endl;
}
}
////Rotate Camera (mode 0)
//Up & Down
else if (key == 's' && mode == 0)
xangle += 5;
else if (key == 'w' && mode == 0)
xangle -= 5;
//Left & Right
else if (key == 'a' && mode == 0)
yangle -= 5;
else if (key == 'd' && mode == 0)
yangle += 5;
////Move Light (mode 1)
//Forward & Back
else if (key == 'w' && mode == 1)
{
if (lightmode == 0)
{
Light1z = Light1z - 1;
//init_surface();
//define_normals();
calc_color();
glutPostRedisplay();
}
else if (lightmode == 1)
Light2z = Light2z - 1;
//init_surface();
}
else if (key == 's' && mode == 1)
{
if (lightmode == 0)
Light1z = Light1z + 1;
else if (lightmode == 1)
Light2z = Light2z + 1;
}
//Strafe
else if (key == 'd' && mode == 1)
{
if (lightmode == 0)
Light1x = Light1x + 1;
else if (lightmode == 1)
Light2x = Light2x + 1;
}
else if (key == 'a' && mode == 1)
{
if (lightmode == 0)
Light1x = Light1x - 1;
else if (lightmode == 1)
Light2x = Light2x - 1;
}
//Up & Down (Cube offset by +0.5 in Y)
else if (key == 'z' && mode == 1)
{
if (lightmode == 0)
Light1y = Light1y + 1;
else if (lightmode == 1)
Light2y = Light2y + 1;
}
else if (key == 'x' && mode == 1)
{
if (lightmode == 0)
Light1y = Light1y - 1;
else if (lightmode == 1)
Light2y = Light2y - 1;
}
//Redraw objects
glutPostRedisplay();
}
//---------------------------------------
// Display callback for OpenGL
//---------------------------------------
void display()
{
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Rotation Code
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(xangle, 1.0, 0.0, 0.0);
glRotatef(yangle, 0.0, 1.0, 0.0);
//Light Code
init_material(Ka, Kd, Ks, 100 * Kp, 0.8, 0.6, 0.4);
//Color Code
calc_color();
//Draw the squares, select column
for (int i = 0; i <= 9; i++)
{
//Select row
for (int j = 0; j <= 9; j++)
{
glBegin(GL_POLYGON);
//Surface starts at top left
//Counter clockwise
glColor3f(R[i][j], G[i][j], B[i][j]);
glNormal3f(Nx[i][j], Ny[i][j], Nz[i][j]);
glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
glColor3f(R[i][j+1], G[i][j+1], B[i][j+1]);
glNormal3f(Nx[i][j+1], Ny[i][j+1], Nz[i][j+1]);
glVertex3f(surfaceX[i][j+1], surfaceY[i][j+1], surfaceZ[i][j+1]);
glColor3f(R[i+1][j+1], G[i+1][j+1], B[i+1][j+1]);
glNormal3f(Nx[i+1][j+1], Ny[i+1][j+1], Nz[i+1][j+1]);
glVertex3f(surfaceX[i+1][j+1], surfaceY[i+1][j+1], surfaceZ[i+1][j+1]);
glColor3f(R[i+1][j], G[i+1][j], B[i+1][j]);
glNormal3f(Nx[i+1][j], Ny[i+1][j], Nz[i+1][j]);
glVertex3f(surfaceX[i+1][j], surfaceY[i+1][j], surfaceZ[i+1][j]);
glEnd();
}
}
//Draw the normals
for (int i = 0; i <= 10; i++)
{
for (int j = 0; j <= 10; j++)
{
glBegin(GL_LINES);
//glColor3f(0.0, 1.0, 1.0);
float length = 1;
glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
glVertex3f(surfaceX[i][j]+length*Nx[i][j],
surfaceY[i][j]+length*Ny[i][j],
surfaceZ[i][j]+length*Nz[i][j]);
glEnd();
}
}
//Marking location of lights
glPointSize(10);
glBegin(GL_POINTS);
glColor3f(Light1r, Light1g, Light1b);
glVertex3f(Light1x, Light1y, Light1z);
glEnd();
glPointSize(10);
glBegin(GL_POINTS);
glColor3f(Light2r, Light2g, Light2b);
glVertex3f(Light2x, Light2y, Light2z);
glEnd();
//+Z = Moving TOWARD camera in opengl
//Origin point for reference
glPointSize(10);
glColor3f(1.0, 1.0, 0.0);
glBegin(GL_POINTS);
glVertex3f(0, 0, 0);
glEnd();
//Assign Color of Lines
float R = 1;
float G = 1;
float B = 1;
glBegin(GL_LINES);
glColor3f(R, G, B);
////Drawing the grid
//Vertical lines
for (int i = 0; i < 11; i++)
{
int b = -5 + i;
glVertex3f(b, 0, -5);
glVertex3f(b, 0, 5);
}
//Horizontal lines
for (int i = 0; i < 11; i++)
{
int b = -5 + i;
glVertex3f(-5,0,b);
glVertex3f(5,0,b);
}
glEnd();
glFlush();
}
//---------------------------------------
// Main program
//---------------------------------------
int main(int argc, char *argv[])
{
srand(time(NULL));
//Print Instructions
std::cout << "Project 3 Controls: " << std::endl;
std::cout << "q switches control mode" << std::endl;
std::cout << "w,a,s,d for camera rotation" << std::endl;
//Required
glutInit(&argc, argv);
//Window will default to a different size without
glutInitWindowSize(500, 500);
//Window will default to a different position without
glutInitWindowPosition(250, 250);
//
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH);
//Required
glutCreateWindow("Project 3");
//Required, calls display function
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
//Required
init();
glutMainLoop();
return 0;
}
1) Have the surface update when the light is moved
You missed to update the position of the light, when the light was moved. Set the light position at the begin of the function display.
Note, when the light position is set by glLightfv(GL_LIGHT0, GL_POSITION, pos), then
pos is multiplied by the current model view matrix.
So the light has to be set after the model view matrix was "cleared" by glLoadIdentity:
void display()
{
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Rotation Code
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// init light
init_light(GL_LIGHT1, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
init_light(GL_LIGHT2, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
glRotatef(xangle, 1.0, 0.0, 0.0);
glRotatef(yangle, 0.0, 1.0, 0.0);
// [...]
}
2) Have the lights and normals remain a static color regardless of shading/light.
Enable lighting before drawing the surface, but disable lighting before drawing the lines and points:
void display()
{
// [...]
// switch on lighting
glEnable(GL_LIGHTING);
//Draw the squares, select column
for (int i = 0; i <= 9; i++)
{
// [...]
}
// switch off lighting
glDisable(GL_LIGHTING);
//Draw the normals
for (int i = 0; i <= 10; i++)
{
// [...]
}
// [...]
}

OpenGL: How do I render individual RGB values given a cross product that contains the total light?

I've been working on my own implementation of a Gouraud style shading model, and I've got the rest of it working pretty much how I want it, but the problem I've run into is it only shows white light. The calc_color function is where this operation is being performed. The Color variable represents the total light of the R, G and B values for that given location. I've been assigning Color to all three arrays just to get the shading implemented properly, but now that that is complete, I'd like to figure out a way to extract the R, G and B values from that total light value.
I've tried several different things, like taking the total light, and taking a percentage of the Light1r, etc. values but it always ends up looking strange or too bright.
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#ifdef MAC
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
using namespace std;
//Camera variables
int xangle = -270;
int yangle = 0;
//Control Modes (Rotate mode by default)
int mode = 0;
int lightmode = 0;
//Player Position (Y offset so it would not be straddling the grid)
float cubeX = 0;
float cubeY = 0.5;
float cubeZ = 0;
//Vertex arrays for surface
float surfaceX [12][12];
float surfaceY [12][12];
float surfaceZ [12][12];
//Surface Normal arrays
float Nx[11][11];
float Ny[11][11];
float Nz[11][11];
//Color arrays
float R[11][11];
float G[11][11];
float B[11][11];
//Light position and color variables
float Light1x = 0;
float Light1y = 5;
float Light1z = 0;
float Light1r = 0;
float Light1g = 1;
float Light1b = 0;
float Light2x = -5;
float Light2y = 5;
float Light2z = -5;
float Light2r = 0;
float Light2g = 1;
float Light2b = 0;
//Random number generator
float RandomNumber(float Min, float Max)
{
return ((float(rand()) / float(RAND_MAX)) * (Max - Min)) + Min;
}
//---------------------------------------
// Initialize surface
//---------------------------------------
void init_surface()
{
//Initialize X, select column
for (int i = 0; i < 12; i++)
{
//Select row
//Surface is +1 so the far right normal will be generated correctly
for (int j = 0; j < 12; j++)
{
//-5 to compensate for negative coordinate values
surfaceX[i][j] = i-5;
//Generate random surface height
surfaceY[i][j] = RandomNumber(5, 7) - 5;
//surfaceY[i][j] = 0;
surfaceZ[i][j] = j-5;
}
}
}
void define_normals()
{
//Define surface normals
for (int i = 0; i < 11; i++)
{
for (int j = 0; j < 11; j++)
{
//Get two tangent vectors
float Ix = surfaceX[i+1][j] - surfaceX[i][j];
float Iy = surfaceY[i+1][j] - surfaceY[i][j];
float Iz = surfaceZ[i+1][j] - surfaceZ[i][j];
float Jx = surfaceX[i][j+1] - surfaceX[i][j];
float Jy = surfaceY[i][j+1] - surfaceY[i][j];
float Jz = surfaceZ[i][j+1] - surfaceZ[i][j];
//Do cross product, inverted for upward normals
Nx[i][j] = - Iy * Jz + Iz * Jy;
Ny[i][j] = - Iz * Jx + Ix * Jz;
Nz[i][j] = - Ix * Jy + Iy * Jx;
//Original vectors
//Nx[i][j] = Iy * Jz - Iz * Jy;
//Ny[i][j] = Iz * Jx - Ix * Jz;
//Nz[i][j] = Ix * Jy - Iy * Jx;
float length = sqrt(
Nx[i][j] * Nx[i][j] +
Ny[i][j] * Ny[i][j] +
Nz[i][j] * Nz[i][j]);
if (length > 0)
{
Nx[i][j] /= length;
Ny[j][j] /= length;
Nz[i][j] /= length;
}
}
}
}
void calc_color()
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
//Calculate light vector
//Light position, hardcoded for now 0,1,1
float Lx = Light1x - surfaceX[i][j];
float Ly = Light1y - surfaceY[i][j];
float Lz = Light1z - surfaceZ[i][j];
float length = sqrt(Lx * Lx + Ly * Ly + Lz * Lz);
if (length > 0)
{
Lx /= length;
Ly /= length;
Lz /= length;
}
//std::cout << "Lx: " << Lx << std::endl;
//std::cout << "Ly: " << Ly << std::endl;
//std::cout << "Lz: " << Lz << std::endl;
//Grab surface normals
//These are Nx,Ny,Nz due to compiler issues
float Na = Nx[i][j];
float Nb = Ny[i][j];
float Nc = Nz[i][j];
//std::cout << "Na: " << Na << std::endl;
//std::cout << "Nb: " << Nb << std::endl;
//std::cout << "Nc: " << Nc << std::endl;
//Do cross product
float Color = (Na * Lx) + (Nb * Ly) + (Nc * Lz);
std::cout << "Color: " << Color << std::endl;
//if (Color > 0)
//{
// Color = Color / 100;
//}
//Percent of light color
//float Ramt = (Light1r/2) / Color;
//float Gamt = (Light1g/2) / Color;
//float Bamt = (Light1b/2) / Color;
//R[i][j] = Ramt * Color;
//G[i][j] = Gamt * Color;
//B[i][j] = Bamt * Color;
R[i][j] = Color;
G[i][j] = Color;
B[i][j] = Color;
}
}
}
//---------------------------------------
// Init function for OpenGL
//---------------------------------------
void init()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//Viewing Window Modified
glOrtho(-7.0, 7.0, -7.0, 7.0, -7.0, 7.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//Rotates camera
//glRotatef(30.0, 1.0, 1.0, 1.0);
glEnable(GL_DEPTH_TEST);
//Project 3 code
init_surface();
define_normals();
//Shading code
// glShadeModel(GL_SMOOTH);
// glEnable(GL_NORMALIZE);
//X,Y,Z - R,G,B
// init_light(GL_LIGHT1, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
// init_light(GL_LIGHT2, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
//init_light(GL_LIGHT2, 0, 1, 0, 0.5, 0.5, 0.5);
}
void keyboard(unsigned char key, int x, int y)
{
///TODO: allow user to change color of light
//Controls
//Toggle Mode
if (key == 'q')
{
if(mode == 0)
{
mode = 1;
std::cout << "Switched to Light mode (" << mode << ")" << std::endl;
}
else if(mode == 1)
{
mode = 0;
std::cout << "Switched to Rotate mode (" << mode << ")" << std::endl;
}
}
//Toggle light control
else if (key == 'e' && mode == 1)
{
if(lightmode == 0)
{
lightmode = 1;
std::cout << "Switched to controlling light 2 (" << lightmode << ")" << std::endl;
}
else if(lightmode == 1)
{
lightmode = 0;
std::cout << "Switched to controlling light 1 (" << lightmode << ")" << std::endl;
}
}
////Rotate Camera (mode 0)
//Up & Down
else if (key == 's' && mode == 0)
xangle += 5;
else if (key == 'w' && mode == 0)
xangle -= 5;
//Left & Right
else if (key == 'a' && mode == 0)
yangle -= 5;
else if (key == 'd' && mode == 0)
yangle += 5;
////Move Light (mode 1)
//Forward & Back
else if (key == 'w' && mode == 1)
{
if (lightmode == 0)
{
Light1z = Light1z - 1;
//init_surface();
//define_normals();
//calc_color();
//glutPostRedisplay();
}
else if (lightmode == 1)
Light2z = Light2z - 1;
//init_surface();
}
else if (key == 's' && mode == 1)
{
if (lightmode == 0)
Light1z = Light1z + 1;
else if (lightmode == 1)
Light2z = Light2z + 1;
}
//Strafe
else if (key == 'd' && mode == 1)
{
if (lightmode == 0)
Light1x = Light1x + 1;
else if (lightmode == 1)
Light2x = Light2x + 1;
}
else if (key == 'a' && mode == 1)
{
if (lightmode == 0)
Light1x = Light1x - 1;
else if (lightmode == 1)
Light2x = Light2x - 1;
}
//Up & Down (Cube offset by +0.5 in Y)
else if (key == 'z' && mode == 1)
{
if (lightmode == 0)
Light1y = Light1y + 1;
else if (lightmode == 1)
Light2y = Light2y + 1;
}
else if (key == 'x' && mode == 1)
{
if (lightmode == 0)
Light1y = Light1y - 1;
else if (lightmode == 1)
Light2y = Light2y - 1;
}
//Redraw objects
glutPostRedisplay();
}
//---------------------------------------
// Display callback for OpenGL
//---------------------------------------
void display()
{
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Rotation Code
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(xangle, 1.0, 0.0, 0.0);
glRotatef(yangle, 0.0, 1.0, 0.0);
//Light Code
// init_material(Ka, Kd, Ks, 100 * Kp, 0.8, 0.6, 0.4);
// init_light(GL_LIGHT1, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
// init_light(GL_LIGHT2, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
// glEnable(GL_LIGHTING);
//Color Code
calc_color();
//Draw the squares, select column
for (int i = 0; i <= 9; i++)
{
//Select row
for (int j = 0; j <= 9; j++)
{
glBegin(GL_POLYGON);
//Surface starts at top left
//Counter clockwise
glColor3f(R[i][j], G[i][j], B[i][j]);
std::cout << R[i][j] << " " << G[i][j] << " " << B[i][j] << endl;
// glNormal3f(Nx[i][j], Ny[i][j], Nz[i][j]);
glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
glColor3f(R[i][j+1], G[i][j+1], B[i][j+1]);
// glNormal3f(Nx[i][j+1], Ny[i][j+1], Nz[i][j+1]);
glVertex3f(surfaceX[i][j+1], surfaceY[i][j+1], surfaceZ[i][j+1]);
glColor3f(R[i+1][j+1], G[i+1][j+1], B[i+1][j+1]);
// glNormal3f(Nx[i+1][j+1], Ny[i+1][j+1], Nz[i+1][j+1]);
glVertex3f(surfaceX[i+1][j+1], surfaceY[i+1][j+1], surfaceZ[i+1][j+1]);
glColor3f(R[i+1][j], G[i+1][j], B[i+1][j]);
// glNormal3f(Nx[i+1][j], Ny[i+1][j], Nz[i+1][j]);
glVertex3f(surfaceX[i+1][j], surfaceY[i+1][j], surfaceZ[i+1][j]);
glEnd();
}
}
// glDisable(GL_LIGHTING);
//Draw the normals
for (int i = 0; i <= 10; i++)
{
for (int j = 0; j <= 10; j++)
{
glBegin(GL_LINES);
glColor3f(0.0, 1.0, 1.0);
float length = 1;
glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
glVertex3f(surfaceX[i][j]+length*Nx[i][j],
surfaceY[i][j]+length*Ny[i][j],
surfaceZ[i][j]+length*Nz[i][j]);
glEnd();
}
}
//Marking location of lights
glPointSize(10);
glBegin(GL_POINTS);
glColor3f(Light1r, Light1g, Light1b);
glVertex3f(Light1x, Light1y, Light1z);
glEnd();
glPointSize(10);
glBegin(GL_POINTS);
glColor3f(Light2r, Light2g, Light2b);
glVertex3f(Light2x, Light2y, Light2z);
glEnd();
//+Z = Moving TOWARD camera in opengl
//Origin point for reference
glPointSize(10);
glColor3f(1.0, 1.0, 0.0);
glBegin(GL_POINTS);
glVertex3f(0, 0, 0);
glEnd();
//Assign Color of Lines
float R = 1;
float G = 1;
float B = 1;
glBegin(GL_LINES);
glColor3f(R, G, B);
////Drawing the grid
//Vertical lines
for (int i = 0; i < 11; i++)
{
int b = -5 + i;
glVertex3f(b, 0, -5);
glVertex3f(b, 0, 5);
}
//Horizontal lines
for (int i = 0; i < 11; i++)
{
int b = -5 + i;
glVertex3f(-5,0,b);
glVertex3f(5,0,b);
}
glEnd();
glFlush();
}
//---------------------------------------
// Main program
//---------------------------------------
int main(int argc, char *argv[])
{
srand(time(NULL));
//Print Instructions
std::cout << "Project 3 Controls: " << std::endl;
std::cout << "q switches control mode" << std::endl;
std::cout << "w,a,s,d for camera rotation" << std::endl;
//Required
glutInit(&argc, argv);
//Window will default to a different size without
glutInitWindowSize(500, 500);
//Window will default to a different position without
glutInitWindowPosition(250, 250);
//
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH);
//Required
glutCreateWindow("Project 3");
//Required, calls display function
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
//Required
init();
glutMainLoop();
return 0;
}
A common formula to calculate a a diffuse light is to calculate the Dot product of the normal vector of the surface and the vector to from the surface to the light source. See How does this faking the light work on aerotwist?.
kd = max(0, L dot N)
To get the color of the light, the RGB values are component wise multiplied by the diffuse coefficient:
(Cr, Cg, Cb) = (LCr, LCg, LCb) * kd
If there are multiple light sources, then the light colors are summed:
(Cr, Cg, Cb) = (LC1r, LC1g, LC1b) * max(0, L1 dot N) + (LC2r, LC2g, LC2b) * max(0, L2 dot N)
Note, if the surface (material) has an additional color, then ths color would have to be component wise multiplied to the final color:
(Cr, Cg, Cb) = (Cr, Cg, Cb) * (CMr, CMg, CMb)
Write a function which calculates the light for 1 single light source and add the light to the final color:
void add_light_color(int i, int j, float lpx, float lpy, float lpz, float lcr, float lcg, float lcb)
{
float Lx = lpx - surfaceX[i][j];
float Ly = lpy - surfaceY[i][j];
float Lz = lpz - surfaceZ[i][j];
float length = sqrt(Lx * Lx + Ly * Ly + Lz * Lz);
if (length <= 0.0)
return;
float kd = Lx/length * Nx[i][j] + Ly/length * Ny[i][j] + Ly/length * Ny[i][j];
if ( kd <= 0.0 )
return;
R[i][j] += kd * lcr;
G[i][j] += kd * lcg;
B[i][j] += kd * lcb;
}
Traverse the filed of attributes, set each color (0, 0, 0) and use the above function to add the color form each light source:
void calc_color()
{
float lp1[] = {Light1x, Light1y, Light1z};
float lp2[] = {Light2x, Light2y, Light2z};
float lc1[] = {Light1r, Light1g, Light1b};
float lc2[] = {Light2r, Light2g, Light2b};
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
R[i][j] = G[i][j] = B[i][j] = 0.0;
add_light_color(i, j, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
add_light_color(i, j, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
}
}
}
The result for the following light color settings:
float Light1r = 1;
float Light1g = 0;
float Light1b = 0;
float Light2r = 0;
float Light2g = 1;
float Light2b = 0;
I recommend to write your first Shader program, which does per fragment lighting and consists of a Vertex Shader and a Fragment Shader.
The program has to use GLSL version 2.00 (OpenGL Shading Language 1.20 Specification). This program can access the Fixed function attributes by the built in variables gl_Vertex, gl_Normal and gl_Color, as the fixed function matrices gl_NormalMatrix, gl_ModelViewMatrix and gl_ModelViewProjectionMatrix and the function ftransform().
See als Built in Vertex Attributes and Hello World in GLSL.
Further the program has to use Uniform Variables for the light colors and positions.
The Vertex shader transforms the model space coordinates and vectors to view space and pass the to the fragment shader by Varying Variables:
std::string vertex_shader = R"(
#version 120
uniform vec3 u_light_pos_1;
uniform vec3 u_light_pos_2;
varying vec3 v_pos;
varying vec3 v_nv;
varying vec4 v_color;
varying vec3 v_light_pos1;
varying vec3 v_light_pos2;
void main()
{
v_pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
v_nv = gl_NormalMatrix * gl_Normal;
v_color = gl_Color;
v_light_pos1 = (gl_ModelViewMatrix * vec4(u_light_pos_1, 1.0)).xyz;
v_light_pos2 = (gl_ModelViewMatrix * vec4(u_light_pos_2, 1.0)).xyz;
gl_Position = ftransform();
}
)";
The fragment shader dose the per Fragment Light calculations in view space:
std::string fragment_shader = R"(
#version 120
varying vec3 v_pos;
varying vec3 v_nv;
varying vec4 v_color;
varying vec3 v_light_pos1;
varying vec3 v_light_pos2;
uniform vec3 u_light_col_1;
uniform vec3 u_light_col_2;
void main()
{
vec3 N = normalize(v_nv);
vec3 L1 = normalize(v_light_pos1 - v_pos);
vec3 L2 = normalize(v_light_pos2 - v_pos);
float kd_1 = max(0.0, dot(L1, N));
float kd_2 = max(0.0, dot(L2, N));
vec3 light_sum = kd_1 * u_light_col_1 + kd_2 * u_light_col_2;
gl_FragColor = vec4(v_color.rgb * light_sum, v_color.a);
}
)";
Compile the shader stages
GLuint generate_shader(GLenum stage, const std::string &source)
{
GLuint shader_obj = glCreateShader(stage);
const char *srcCodePtr = source.c_str();
glShaderSource(shader_obj, 1, &srcCodePtr, nullptr);
glCompileShader(shader_obj);
GLint status;
glGetShaderiv(shader_obj, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint maxLen;
glGetShaderiv(shader_obj, GL_INFO_LOG_LENGTH, &maxLen);
std::vector< char >log( maxLen );
GLsizei len;
glGetShaderInfoLog(shader_obj, maxLen, &len, log.data());
std::cout << "compile error:" << std::endl << log.data() << std::endl;
}
return shader_obj;
}
and link the program.
GLuint generate_program(const std::string &vert_sh, const std::string &frag_sh)
{
std::cout << "compile vertex shader" << std::endl;
GLuint vert_obj = generate_shader(GL_VERTEX_SHADER, vert_sh);
std::cout << "compile fragment shader" << std::endl;
GLuint frag_obj = generate_shader(GL_FRAGMENT_SHADER, frag_sh);
std::cout << "link shader program" << std::endl;
GLuint program_obj = glCreateProgram();
glAttachShader(program_obj, vert_obj);
glAttachShader(program_obj, frag_obj);
glLinkProgram(program_obj);
GLint status;
glGetProgramiv(program_obj, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint maxLen;
glGetProgramiv(program_obj, GL_INFO_LOG_LENGTH, &maxLen);
std::vector< char >log( maxLen );
GLsizei len;
glGetProgramInfoLog(program_obj, maxLen, &len, log.data());
std::cout << "link error:" << std::endl << log.data() << std::endl;
}
glDeleteShader(vert_obj);
glDeleteShader(frag_obj);
return program_obj;
}
Further get the uniform locations by glGetUniformLocation in the function init:
GLuint diffuse_prog_obj = 0;
GLint loc_l_pos[] = {-1, -1};
GLint loc_l_col[] = {-1, -1};
void init()
{
diffuse_prog_obj = generate_program(vertex_shader, fragment_shader);
loc_l_pos[0] = glGetUniformLocation(diffuse_prog_obj, "u_light_pos_1");
loc_l_pos[1] = glGetUniformLocation(diffuse_prog_obj, "u_light_pos_2");
loc_l_col[0] = glGetUniformLocation(diffuse_prog_obj, "u_light_col_1");
loc_l_col[1] = glGetUniformLocation(diffuse_prog_obj, "u_light_col_2");
// [...]
}
The shader program can be used by glUseProgram. The uniforms are set by glUniform*.
Beside the vertex coordinates, the normal vector attributes have to be set per vertex, to make the light calculations proper work. But it is sufficient to set a single color for the entire mesh:
void display()
{
// [...]
// install program
glUseProgram(diffuse_prog_obj);
// set light positions and colors
glUniform3f(loc_l_pos[0], Light1x, Light1y, Light1z);
glUniform3f(loc_l_pos[1], Light2x, Light2y, Light2z);
glUniform3f(loc_l_col[0], Light1r, Light1g, Light1b);
glUniform3f(loc_l_col[1], Light2r, Light2g, Light2b);
// set object color
glColor3f(1, 1, 0.5);
//Draw the squares, select column
for (int i = 0; i <= 9; i++)
{
//Select row
for (int j = 0; j <= 9; j++)
{
glBegin(GL_POLYGON);
std::cout << R[i][j] << " " << G[i][j] << " " << B[i][j] << endl;
glNormal3f(Nx[i][j], Ny[i][j], Nz[i][j]);
glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
glNormal3f(Nx[i][j+1], Ny[i][j+1], Nz[i][j+1]);
glVertex3f(surfaceX[i][j+1], surfaceY[i][j+1], surfaceZ[i][j+1]);
glNormal3f(Nx[i+1][j+1], Ny[i+1][j+1], Nz[i+1][j+1]);
glVertex3f(surfaceX[i+1][j+1], surfaceY[i+1][j+1], surfaceZ[i+1][j+1]);
glNormal3f(Nx[i+1][j], Ny[i+1][j], Nz[i+1][j]);
glVertex3f(surfaceX[i+1][j], surfaceY[i+1][j], surfaceZ[i+1][j]);
glEnd();
}
}
// invalidate installed program
glUseProgram(0);
// [...]
}
See the preview of you program, with the applied suggestions:

OpenGL Cone Function Not Drawing Correctly

I found an example online that shows how to draw a cone in OpenGL, which is located here: It was written in C++, and so I translated it to C#. Here is the new code:
public void RenderCone(Vector3 d, Vector3 a, float h, float rd, int n)
{
Vector3 c = new Vector3(a + (-d * h));
Vector3 e0 = Perp(d);
Vector3 e1 = Vector3.Cross(e0, d);
float angInc = (float)(360.0 / n * GrimoireMath.Pi / 180);
// calculate points around directrix
List<Vector3> pts = new List<Vector3>();
for (int i = 0; i < n; ++i)
{
float rad = angInc * i;
Vector3 p = c + (((e0 * (float)Math.Cos((rad)) + (e1 * (float)Math.Sin(rad))) * rd));
pts.Add(p);
}
// draw cone top
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(a);
for (int i = 0; i < n; ++i)
{
GL.Vertex3(pts[i]);
}
GL.End();
// draw cone bottom
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(c);
for (int i = n - 1; i >= 0; --i)
{
GL.Vertex3(pts[i]);
}
GL.End();
}
public Vector3 Perp(Vector3 v)
{
float min = Math.Abs(v.X);
Vector3 cardinalAxis = new Vector3(1, 0, 0);
if (Math.Abs(v.Y) < min)
{
min = Math.Abs(v.Y);
cardinalAxis = new Vector3(0, 1, 0);
}
if (Math.Abs(v.Z) < min)
{
cardinalAxis = new Vector3(0, 0, 1);
}
return Vector3.Cross(v, cardinalAxis);
}
I think I am using the parameters correctly(the page isnt exactly coherent in terms of actual function-usage). Here is the legend that the original creator supplied:
But when I enter in the following as parameters:
RenderCone(new Vector3(0.0f, 1.0f, 0.0f), new Vector3(1.0f, 1.0f, 1.0f), 20.0f, 10.0f, 8);
I receive this(Wireframe enabled):
As you can see, I'm missing a slice, either at the very beginning, or the very end. Does anyone know what's wrong with this method? Or what I could be doing wrong that would cause an incomplete cone?
// draw cone bottom
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(c);
for (int i = n - 1; i >= 0; --i)
{
GL.Vertex3(pts[i]);
}
GL.End();
That connects all vertices to each other and center but there is one connection missing. There is nothing the specifies connection from first to last vertex. Adding GL.Vertex3(pts[n-1]); after loop would add the missing connection.
The Solution was actually extremely simple, I needed to increase the number of slices by 1. Pretty special if you ask me.
public void RenderCone(Vector3 baseToApexLength, Vector3 apexLocation, float height, float radius, int slices)
{
Vector3 c = new Vector3(apexLocation + (-baseToApexLength * height));
Vector3 e0 = Perpendicular(baseToApexLength);
Vector3 e1 = Vector3.Cross(e0, baseToApexLength);
float angInc = (float)(360.0 / slices * GrimoireMath.Pi / 180);
slices++; // this was the fix for my problem.
/**
* Compute the Vertices around the Directrix
*/
Vector3[] vertices = new Vector3[slices];
for (int i = 0; i < vertices.Length; ++i)
{
float rad = angInc * i;
Vector3 p = c + (((e0 * (float)Math.Cos((rad)) + (e1 * (float)Math.Sin(rad))) * radius));
vertices[i] = p;
}
/**
* Draw the Top of the Cone.
*/
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(apexLocation);
for (int i = 0; i < slices; ++i)
{
GL.Vertex3(vertices[i]);
}
GL.End();
/**
* Draw the Base of the Cone.
*/
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(c);
for (int i = slices - 1; i >= 0; --i)
{
GL.Vertex3(vertices[i]);
}
GL.End();
}

Draw A Sphere With Opengl

I draw the sphere by OpenGL.
This program can draw sphere when "SLIED = STACK".
But It's doesn't work when "SLIED not equal STACK".
I may be mistake "index array".
I want to use VBO and DMA(Dynamic memory allocate.use flat array).
But If You have another good idea, teach me.
//#pragma comment(linker, "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
#pragma comment(lib,"glew32.lib")
#include <gl/glew.h>
#include <math.h>
#include <GL/freeglut/freeglut.h>
#define WIDTH 640
#define HEIGHT 480
#define M_PI 3.14159265358979323846
float angle=0.0f;
//functions----------------------------------------------//
void Shere_dma(double radius, int nSlice, int nStack);
void setSphereData(double radius, int nSlice, int nStack);
void DrawSphereVBO(void);
//-------------------------------------------------------//
//variabless---------------------------------------------//
int SLICE=0;
int STACK=0;
GLuint SVboId[3];
GLfloat* Svertex;//Vertex
GLfloat* Snormal;//Normal
GLfloat* Scolor; //Color
GLuint* indices_top; //TOP indices
GLuint* indices_bottom; //BOTTOM indies
GLuint* indices_side; //SIDE indices
//-------------------------------------------------------//
//Memory Allocate
void Shere_dma(double radius, int nSlice, int nStack){
Svertex = new GLfloat[(nSlice+1)*(nStack+1)*3];
Snormal = new GLfloat[(nSlice+1)*(nStack+1)*3];
Scolor = new GLfloat[(nSlice+1)*(nStack+1)*3];
indices_top = new GLuint[(nSlice+1)*(nStack+1)*3];
indices_bottom = new GLuint[(nSlice+1)*(nStack+1)*3];
indices_side = new GLuint[(nSlice+1)*(nStack+1)*4];
}
//inputData
void setSphereData(double radius, int nSlice, int nStack){
double phi; //Azimuth
double theta; //long
int coordinates=0;
int Slice_current_point_no=0;
int Slice_next_point_no=0;
const int x = 0;
const int y = 1;
const int z = 2;
const int p1 = 0;
const int p2 = 1;
const int p3 = 2;
const int p4 = 3;
//Vertex
for(int i = 0;i <= nSlice;i++)
{
phi = 2.0 * M_PI * (double)i / (double)nSlice;
for(int j = 0;j <= nStack;j++)
{
theta = M_PI * (double)j / (double)nStack;
Svertex[coordinates+x] = (float)(radius * sin(theta) * cos(phi)); //x
Svertex[coordinates+y] = (float)(radius * sin(theta) * sin(phi)); //y
Svertex[coordinates+z] = (float)(radius * cos(theta)); //z
Snormal[coordinates+x] = (float)(radius * sin(theta) * cos(phi)); //x
Snormal[coordinates+y] = (float)(radius * sin(theta) * sin(phi)); //y
Snormal[coordinates+z] = (float)(radius * cos(theta)); //z
Scolor[coordinates+x] = 1.0; //x
Scolor[coordinates+y] = 0.0; //y
Scolor[coordinates+z] = 0.0; //z
coordinates += 3;
}
}
//TOP
coordinates = 0;
Slice_current_point_no = 0;
Slice_next_point_no = nSlice;
for(int i = 0; i < nSlice; i++){
indices_top[coordinates+p1] = Slice_current_point_no;
indices_top[coordinates+p2] = indices_top[coordinates]+1;
indices_top[coordinates+p3] = Slice_next_point_no+2;
coordinates+=3;
Slice_current_point_no += nSlice+1;
Slice_next_point_no += nSlice+1;
}
//BOTTOM
coordinates =0;
Slice_current_point_no = 0;
Slice_next_point_no = nSlice;
for(int i = 0; i < nSlice; i++){
indices_bottom[coordinates+p1] = Slice_current_point_no+(nStack-1);
indices_bottom[coordinates+p2] = indices_bottom[coordinates]+1;
indices_bottom[coordinates+p3] = Slice_next_point_no+(nStack);
coordinates+=3;
Slice_current_point_no += nSlice+1;
Slice_next_point_no += nSlice+1;
}
//↓May be wrong ********************************************************************************//
//SIDE
coordinates=0;
Slice_current_point_no = 0;
Slice_next_point_no = nSlice+1;
for(int i=0; i < nSlice;i++){
for(int j=1; j < nStack-1; j++){
indices_side[coordinates+p1] = Slice_current_point_no+j;
indices_side[coordinates+p2] = indices_side[coordinates]+1;
indices_side[coordinates+p3] = Slice_next_point_no+(j+1);
indices_side[coordinates+p4] = Slice_next_point_no+j;
coordinates+=4;
}
Slice_current_point_no += nSlice+1;
Slice_next_point_no += nSlice+1;
}
//↑May be wrong ********************************************************************************//
glGenBuffers(3,&SVboId[0]);
//Vertex
glBindBuffer(GL_ARRAY_BUFFER,SVboId[0]);
glBufferData(GL_ARRAY_BUFFER,sizeof(GLfloat)*(nSlice+1)*(nStack+1)*3,
Svertex,GL_DYNAMIC_DRAW);
//Normal
glBindBuffer(GL_ARRAY_BUFFER,SVboId[1]);
glBufferData(GL_ARRAY_BUFFER,sizeof(GLfloat)*(nSlice+1)*(nStack+1)*3,
Snormal,GL_DYNAMIC_DRAW);
//Color
glBindBuffer(GL_ARRAY_BUFFER,SVboId[2]);
glBufferData(GL_ARRAY_BUFFER,sizeof(GLfloat)*(nSlice+1)*(nStack+1)*3,
Scolor,GL_STREAM_DRAW);
}
//Draw
void DrawSphereVBO(void)
{
int point=0;
//Enable
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
//Vertex
glBindBuffer(GL_ARRAY_BUFFER,SVboId[0]);
glVertexPointer(3, GL_FLOAT, 0, 0);
//Normal
glBindBuffer(GL_ARRAY_BUFFER,SVboId[1]);
glNormalPointer(GL_FLOAT, 0, 0);
//Color
glBindBuffer(GL_ARRAY_BUFFER,SVboId[2]);
glColorPointer(3,GL_FLOAT, 0, 0);
//---------------------------------Draw---------------------------------------------------//
//TOP
for(int i=0; i<SLICE;i++){
glDrawRangeElements(GL_TRIANGLES, point, point+2, 3, GL_UNSIGNED_INT, indices_top+i*3);
point+=3;
}
//BOTTOM
point=0;
for(int i=0; i<SLICE;i++){
glDrawRangeElements(GL_TRIANGLES, point, point+2, 3, GL_UNSIGNED_INT, indices_bottom+i*3);
point+=3;
}
//↓May be wrong ********************************************************************************//
//SIDE
point=0;
for(int i=0; i< (SLICE*(STACK-2));i++){
glDrawRangeElements(GL_QUADS, point, point+3, 4, GL_UNSIGNED_INT, indices_side+i*4);
point+=4;
}
//↑May be wrong ********************************************************************************//
//---------------------------------Draw---------------------------------------------------//
//Disable
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0,0,WIDTH,HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30.0, (double)WIDTH / (double)HEIGHT, 1.0, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(5.0, 5.0,5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
glRotatef(angle,0.0f,1.0f,0.0f);
DrawSphereVBO();
glutSwapBuffers();
}
void idle(void)
{
glutPostRedisplay();
angle+=0.2f;
}
void Init(){
glewInit();
glClearColor(1.0, 1.0, 1.0, 1.0);
glEnable(GL_DEPTH_TEST);
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_NORMALIZE);
SLICE = 5;
STACK = 5;
Shere_dma(1,SLICE,STACK); //MemoryAllocate
setSphereData(1,SLICE,STACK); //InputData
}
int main(int argc, char *argv[])
{
glutInitWindowPosition(100, 100);
glutInitWindowSize(WIDTH, HEIGHT);
glutInit(&argc, argv);
glutCreateWindow("VBO");
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutDisplayFunc(display);
glutIdleFunc(idle);
Init();
glutMainLoop();
return 0;
}
I refer to this code.
void drawSphere(double radius, int nSlice, int nStack)
{
int i, j;
double phi; //
double theta; //long
float p[31][31][3]; //Vertex
float *p1,*p2,*p3,*p4;
if(nSlice > 30) nSlice = 30;
if(nStack > 30) nStack = 30;
//Vertex
for(i = 0;i <= nSlice;i++)
{
phi = 2.0 * M_PI * (double)i / (double)nSlice;
for(j = 0;j <= nStack;j++)
{
theta = M_PI * (double)j / (double)nStack;
p[i][j][0] = (float)(radius * sin(theta) * cos(phi));//x
p[i][j][1] = (float)(radius * sin(theta) * sin(phi));//y
p[i][j][2] = (float)(radius * cos(theta)); //z
}
}
//Top(j=0)
for(i = 0;i < nSlice; i++)
{
p1 = p[i][0]; p2 = p[i][1];
p3 = p[i+1][1];
glBegin(GL_TRIANGLES);
glNormal3fv(p1); glVertex3fv(p1);
glNormal3fv(p2); glVertex3fv(p2);
glNormal3fv(p3); glVertex3fv(p3);
glEnd();
}
//Bottom
j=nStack-1;
for(i = 0;i < nSlice; i++)
{
p1 = p[i][j]; p2 = p[i][j+1];
p3 = p[i+1][j];
glBegin(GL_TRIANGLES);
glNormal3fv(p1); glVertex3fv(p1);
glNormal3fv(p2); glVertex3fv(p2);
glNormal3fv(p3); glVertex3fv(p3);
glEnd();
}
for(i = 0;i < nSlice;i++){
for(j = 1;j < nStack-1; j++)
{
p1 = p[i][j]; p2 = p[i][j+1];
p3 = p[i+1][j+1]; p4 = p[i+1][j];
glBegin(GL_QUADS);
glNormal3fv(p1); glVertex3fv(p1);
glNormal3fv(p2); glVertex3fv(p2);
glNormal3fv(p3); glVertex3fv(p3);
glNormal3fv(p4); glVertex3fv(p4);
glEnd();
}
}
}
I've written a tutorial on generating sphere meshes - it doesn't come with source code, but describes the process and walks you through the issues you'll encounter. You can find the tutorial at: http://sol.gfxile.net/sphere/index.html
I would recommend using spherical coordinates using two angles phi (elevation) and theta (around) or by using the equation of the sphere: x2 + y2 + z2 = r2
were you keep one of the values constant and increment later as you replace values in the 2nd to find the 3rd value.