点光源漫反射着色器渲染球形图元
// DiffuseLight.cpp
// OpenGL SuperBible
// Demonstrates simple diffuse lighting
// Program by Richard S. Wright Jr.
#include
// OpenGL toolkit
#include
#include
#include
#include
#include
#include
#ifdef __APPLE__ #include
#else #define FREEGLUT_STATIC #include
#endif GLFrame viewFrame; GLFrustum viewFrustum; GLTriangleBatch sphereBatch; GLMatrixStack modelViewMatrix; GLMatrixStack projectionMatrix; GLGeometryTransform transformPipeline; GLShaderManager shaderManager; GLuint diffuseLightShader; // The diffuse light shader GLint locColor; // The location of the diffuse color GLint locLight; // The location of the Light in eye coordinates GLint locMVP; // The location of the ModelViewProjection matrix uniform GLint locMV; // The location of the ModelView matrix uniform GLint locNM; // The location of the Normal matrix uniform // This function does any needed initialization on the rendering // context. void SetupRC(void) { // Background glClearColor(0.3f, 0.3f, 0.3f, 1.0f ); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); shaderManager.InitializeStockShaders(); viewFrame.MoveForward(4.0f); // Make the sphere gltMakeSphere(sphereBatch, 1.0f, 26, 13); diffuseLightShader = shaderManager.LoadShaderPairWithAttributes("DiffuseLight.vp", "DiffuseLight.fp", 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_NORMAL, "vNormal"); locColor = glGetUniformLocation(diffuseLightShader, "diffuseColor"); locLight = glGetUniformLocation(diffuseLightShader, "vLightPosition"); locMVP = glGetUniformLocation(diffuseLightShader, "mvpMatrix"); locMV = glGetUniformLocation(diffuseLightShader, "mvMatrix"); locNM = glGetUniformLocation(diffuseLightShader, "normalMatrix"); } // Cleanup void ShutdownRC(void) { } // Called to draw scene void RenderScene(void) { static CStopWatch rotTimer; // Clear the window and the depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); modelViewMatrix.PushMatrix(viewFrame); modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds() * 10.0f, 0.0f, 1.0f, 0.0f); GLfloat vEyeLight[] = { -100.0f, 100.0f, 100.0f }; GLfloat vDiffuseColor[] = { 0.0f, 0.0f, 1.0f, 1.0f }; glUseProgram(diffuseLightShader); glUniform4fv(locColor, 1, vDiffuseColor); glUniform3fv(locLight, 1, vEyeLight); glUniformMatrix4fv(locMVP, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix()); glUniformMatrix4fv(locMV, 1, GL_FALSE, transformPipeline.GetModelViewMatrix()); glUniformMatrix3fv(locNM, 1, GL_FALSE, transformPipeline.GetNormalMatrix()); sphereBatch.Draw(); modelViewMatrix.PopMatrix(); glutSwapBuffers(); glutPostRedisplay(); } void ChangeSize(int w, int h) { // Prevent a divide by zero if(h == 0) h = 1; // Set Viewport to window dimensions glViewport(0, 0, w, h); viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f); projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix); } /// // Main entry point for GLUT based programs int main(int argc, char* argv[]) { gltSetWorkingDirectory(argv[0]); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(800, 600); glutCreateWindow("Simple Diffuse Lighting"); glutReshapeFunc(ChangeSize); glutDisplayFunc(RenderScene); GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); return 1; } SetupRC(); glutMainLoop(); ShutdownRC(); return 0; } #version 130 // Incoming per vertex... position and normal in vec4 vVertex; in vec3 vNormal; // Set per batch uniform vec4 diffuseColor; uniform vec3 vLightPosition; uniform mat4 mvpMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; // Color to fragment program smooth out vec4 vVaryingColor; void main(void) { // Get surface normal in eye coordinates vec3 vEyeNormal = normalMatrix * vNormal; // Get vertex position in eye coordinates vec4 vPosition4 = mvMatrix * vVertex; vec3 vPosition3 = vPosition4.xyz / vPosition4.w; // Get vector to light source vec3 vLightDir = normalize(vLightPosition - vPosition3); // Dot product gives us diffuse intensity float diff = max(0.0, dot(vEyeNormal, vLightDir)); // Multiply intensity by diffuse color vVaryingColor.rgb = diff * diffuseColor.rgb; vVaryingColor.a = diffuseColor.a; // Let's not forget to transform the geometry gl_Position = mvpMatrix * vVertex; }#version 130 out vec4 vFragColor; smooth in vec4 vVaryingColor; void main(void) { vFragColor = vVaryingColor; }
一、简单漫射光基础
3D图形中应用最普遍的光线类型是漫射光。漫射光是一种经平面反射的定向光,其强度与光线在表面上的入射角成正比。这样,如果光线直线射向表面的话,物体的表面就要比光线以一个很大的角度倾斜着射向表面的时候亮度高。实际上这就是很多通过照亮物体表面产生阴影(或者改变颜色)的光照模型的漫射光分量。要确定一个指定顶点上光线的强度,需要两个向量。
1、光源的方向。
定向光是指对于所有顶点来说指向光源的都是同一个向量,这种方式在光源距离被照射物体距离很远时(无穷远,例如太阳)非常适用。
点光源可以看成是近距离光源,我们必须在着色器中用经过变化的(视觉坐标)光源位置减去顶点位置,来确定指向光源的位置。(示例就是如此)
2、表面法线
法线向量就是一条指向一个与多边形的前面成90°角方向的线。由于物体表面很多都不是平的,所以要为每个顶点指定一个法向量。
3、顶点照明
顶点上光的强度通过接受到光源的向量和表面法线的向量点乘积来计算。这两个向量需要时单位长度的,返回值是-1到+1之间的值。
当法线和光照向量指向同一方向时,点乘积值为1,光照效果越强。
当法线和光照向量互成90°时,点乘积值为0,光照效果越弱。
当法线和光照向量指向相反方向时,点乘积值为-1,光照效果弱爆了。
所以-1.0到+1.0反应了光照的强度值。
点乘:float intensity = dot(vSurfaceNormal,vLightDirection);
二、Client程序部分源码解析
1、全局变量
GLuintdiffuseLightShader;// 着色器的标识
GLintlocColor;// 获取着色器程序颜色值
GLintlocLight;// 光源的坐标
GLintlocMVP;// 模型视图投影矩阵的统一值
GLintlocMV;// 模型视图矩阵的统一值
GLintlocNM;// 法向量矩阵的统一值
2、函数解析
1)void SetupRC(void)
.......
gltMakeSphere(sphereBatch, 1.0f, 26, 13);//绘制球形
//加载和初始化着色器。传入顶点和片段程序文件;“2”表示顶点程序中包含属性的个数;最后两个参数表明是一个带有顶点和法向量属性的着色器
diffuseLightShader = shaderManager.LoadShaderPairWithAttributes("DiffuseLight.vp", "DiffuseLight.fp", 2, GLT_ATTRIBUTE_VERTEX, "vVertex",
GLT_ATTRIBUTE_NORMAL, "vNormal");
//分别获取着色器程序中的“diffuseColor”、“vLightPosition”、“mvMatrix”、"mvpMatrix"和“normalMatrix”统一值,
locColor = glGetUniformLocation(diffuseLightShader, "diffuseColor");//颜色
locLight = glGetUniformLocation(diffuseLightShader, "vLightPosition");//光源位置
locMVP = glGetUniformLocation(diffuseLightShader, "mvpMatrix");//模型视图投影矩阵
locMV = glGetUniformLocation(diffuseLightShader, "mvMatrix");//模型视图矩阵
locNM = glGetUniformLocation(diffuseLightShader, "normalMatrix");//法向量矩阵
2)void RenderScene(void)
.........
GLfloat vEyeLight[] = { -100.0f, 100.0f, 100.0f };//定义视觉坐标中光源的位置
GLfloat vDiffuseColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };//定义要渲染的颜色(蓝色)
glUseProgram(diffuseLightShader); //使用着色器
glUniform4fv(locColor, 1, vDiffuseColor);//设置顶点着色器颜色统一值
glUniform3fv(locLight, 1, vEyeLight);//设置顶点着色器的视觉坐标系的光源位置坐标
glUniformMatrix4fv(locMVP, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());//将管线得到的模型视图投影矩阵设置着色器的统一值
glUniformMatrix4fv(locMV, 1, GL_FALSE, transformPipeline.GetModelViewMatrix());//将管线得到的模型视图矩阵设置设置着色器的统一值
glUniformMatrix3fv(locNM, 1, GL_FALSE, transformPipeline.GetNormalMatrix());//将管线得到的法线矩阵设置着色器的统一值
sphereBatch.Draw(); //渲染球体
modelViewMatrix.PopMatrix();//模型视图矩阵堆栈出栈,避免对后续图形渲染的影响
3)void ChangeSize(int w, int h)
glViewport(0, 0, w, h);//设置视口
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);//平截头体设置透视投影模式
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());//将得到的投影矩阵压栈到投影矩阵堆栈中
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);//设置管线管理,模型视图矩阵和投影矩阵。(GLTools中可查看源码)
三、Server程序源码解析
DiffuseLight.vp
1、全局变量
in vec4 vVertex; //顶点位置属性值(输入)注意:Client源码使用了gltMakeSphere方法绘制的图元,在此方法内部有顶点的定义
in vec3 vNormal;//法线坐标
注意:这两个属性值对应Client程序SetupRC函数的shaderManager.LoadShaderPairWithAttributes传的参数后面2个带引号的参数
//声明着色器程序使用的统一值,在Client程序可进行设置
uniform vec4diffuseColor; //渲染的颜色
uniform vec3vLightPosition; //光源的位置坐标
uniform mat4mvpMatrix; //模型视图投影矩阵
uniform mat4mvMatrix; //模型视图矩阵
uniform mat3normalMatrix; //法向量矩阵(图元的每个顶点都有法向量)
smooth out vec4 vVaryingColor; //顶点着色器的输出颜色,作为片段着色器的输入
2、函数解析
void main(void)
{
//得到在视坐标系的法线向量
vec3 vEyeNormal = normalMatrix * vNormal;
//得到在视坐标系顶点的位置坐标。注意:除以w分量是规范化
vec4 vPosition4 = mvMatrix * vVertex;
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
//光源相对顶点的方向坐标。用光源的位置坐标减去视坐标系的顶点位置坐标
vec3 vLightDir = normalize(vLightPosition - vPosition3);
//计算光照强度。用视坐标系的法线向量与光源方向坐标点乘。请看上面的介绍
float diff = max(0.0, dot(vEyeNormal, vLightDir));
//将上面得到的光照强度值与渲染的颜色值相乘,可看成是漫射光照射后的渲染效果。alpha值不变
vVaryingColor.rgb = diff * diffuseColor.rgb;
vVaryingColor.a = diffuseColor.a;
gl_Position = mvpMatrix * vVertex;//将顶点坐标转换到模型视图投影坐标系中
}
DiffuseLight.vp
1、全局变量
out vec4 vFragColor;//将要进行光栅化的颜色值(输出)
smooth in vec4 vVaryingColor//从顶端程序传递过来的颜色值(输入)
2、函数解析
void main(void)
{
vFragColor = vVaryingColor;//将从定程序传过来的颜色值复制给输出颜色值
}
四、小结
此次源码添加了模拟光线中的漫射光渲染3D图形的应用,在客户端程序中获取和设置相关的统一值(uniform),在着色器程序中把相关统一值相互处理,输出一个颜色值,在Client应用程序(C++程序)中选择加载着色器程序后,渲染图元。后面再学习ADS光照模型。