(1)簡單漫反射光
3D圖形利用最普遍的光線類型就是漫射光。漫反射光是1種經(jīng)過平面反射的定向光,其強(qiáng)度和光線在表面的入射角成正比。
要肯定1個(gè)指定定點(diǎn)上的光線的強(qiáng)度,我們需要兩個(gè)向量。第1個(gè)向量就是光源的方向。某些光源技術(shù)只是提供1個(gè)指向光源的向量,我們稱之為定向光。對所有頂點(diǎn)來講指向光源的,都是同1個(gè)向量。
注意:這類方式在光源距離被照亮物體非常遠(yuǎn)的情況下是非常適用的。如果照明朝碼提供的是光源的位置,那末我們必須在著色器中使用經(jīng)過變換的視覺坐標(biāo)光源位置減去頂點(diǎn)位置。從而肯定指向光源的向量。
表面法線
我們在漫反射光源中需要的第2個(gè)向量是表面法線。經(jīng)過某個(gè)假想平面上方的頂點(diǎn),并與這個(gè)平面成直角的1條線段,這條線就成為法向量。
注意,每一個(gè)頂點(diǎn)都要制定1個(gè)法向量。但是我們其實(shí)不希望每一個(gè)法向量都和多邊形的表面精確垂直。我們可以將這些表面近似看成是平的,但是結(jié)果會(huì)得到1個(gè)鋸齒狀或多面的表面,我們可以通過“調(diào)劑”表面法線使平面多邊形表面平滑,從而得到平滑表面的錯(cuò)覺。
頂點(diǎn)照明
頂點(diǎn)上光的強(qiáng)度通過接收到光源的向量和表面法線的向量點(diǎn)乘積來計(jì)算。兩個(gè)向量也需要是單位長度,而點(diǎn)乘積將會(huì)返回1個(gè)+⑴之間的值。當(dāng)表面法線和光照向量指向同1個(gè)方向的時(shí)候,將會(huì)出現(xiàn)1個(gè)1值的點(diǎn)乘積。當(dāng)兩個(gè)向量指向相反的方向的時(shí)候,則會(huì)返回⑴,。當(dāng)兩個(gè)向量相互成90度的時(shí)候,返回的點(diǎn)乘積為0,。這個(gè)+⑴之間的值實(shí)際上是這兩個(gè)向量之間夾角的余弦值。
正值意味著光線落在頂點(diǎn)上,這個(gè)值越接近1,則光照效果越強(qiáng),越接近0,那末光照效果越弱。
我們可以用點(diǎn)乘積的值和頂點(diǎn)的1個(gè)色彩值相乘,得到1個(gè)基于頂點(diǎn)光線強(qiáng)度的光照色彩值。在頂點(diǎn)之間對這些色彩值進(jìn)行平滑的著色,有時(shí)候被稱作頂點(diǎn)照明(vertex lighting)或背景著色(Gouraud shading)。在GLSL中,點(diǎn)乘積的部份比較簡單。
float intensity=dot(vSurfaceNormal, vLightDirection);
(2)點(diǎn)光源漫反射著色器
// Simple Diffuse lighting Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
//首先是頂點(diǎn)渲染器
// 輸入頂點(diǎn)和法向量
in vec4 vVertex;
in vec3 vNormal;
//注意:典型情況下,表面法線作為1個(gè)頂點(diǎn)屬性提交,表面法線必須
//進(jìn)行旋轉(zhuǎn)從而使得他的方向在視覺空間以內(nèi)。我們常常傳遞1個(gè)法向矩陣
//這個(gè)值只包括模型視圖矩陣的旋轉(zhuǎn)份量。
//可以通過GLTransformation當(dāng)中的GetNormalMatrix函數(shù)返回這個(gè)值。
// 設(shè)置每一個(gè)批次
uniform vec4 diffuseColor; //球體的色彩
uniform vec3 vLightPosition;//光源位置的視覺坐標(biāo)
uniform mat4 mvpMatrix;//模型視圖投影矩陣
uniform mat4 mvMatrix;//模型視圖矩陣
uniform mat3 normalMatrix;//
// 片斷程序所需要的色彩,經(jīng)過平滑著色的色彩值。
smooth out vec4 vVaryingColor;
void main(void)
{
// 得到視覺坐標(biāo)系的表面法線向量
vec3 vEyeNormal = normalMatrix * vNormal;
// 得到視覺坐標(biāo)系的頂點(diǎn)坐標(biāo)
vec4 vPosition4 = mvMatrix * vVertex;
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
// 得到光源的向量
vec3 vLightDir = normalize(vLightPosition - vPosition3);
// 得到點(diǎn)乘積所得到的漫反射強(qiáng)度
float diff = max(0.0, dot(vEyeNormal, vLightDir));
// 強(qiáng)度乘以漫反射色彩,得到漫反射光線色彩
vVaryingColor.rgb = diff * diffuseColor.rgb;
vVaryingColor.a = diffuseColor.a;
// 最后對多邊形進(jìn)行變換,讓模型透視投影矩陣對當(dāng)前
//頂點(diǎn)進(jìn)行變換。
gl_Position = mvpMatrix * vVertex;
}
點(diǎn)渲染shader程序,在主程序工程文件下,以vp格式的文件存在。
#version 130
out vec4 vFragColor;
smooth in vec4 vVaryingColor;
void main(void)
{
//將點(diǎn)著色器當(dāng)中的輸出vVaryingColor直接分配給輸出
//片斷色彩。
vFragColor = vVaryingColor;
}
片斷著色器如上,只需要對其的片斷色彩進(jìn)行賦值就能夠了。
#pragma comment(lib,"GLTools.lib")
#include <GLTools.h> // OpenGL toolkit
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>
#include <GL/glut.h>
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
//任務(wù):設(shè)置背景色彩
//加載渲染框架、加載渲染器程序代碼、得到統(tǒng)1值的location
void SetupRC(void)
{
// 背景色彩的設(shè)置
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
//遮擋剔除和深度測試
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
shaderManager.InitializeStockShaders();
viewFrame.MoveForward(4.0f);
// 創(chuàng)造1個(gè)球體
gltMakeSphere(sphereBatch, 1.0f, 26, 13);
//加載渲染框架,同時(shí)加載渲染器代碼,并且將兩個(gè)基本點(diǎn)的屬性傳入到頂點(diǎn)著色器中。
diffuseLightShader = shaderManager.LoadShaderPairWithAttributes("DiffuseLight.vp", "DiffuseLight.fp", 2, GLT_ATTRIBUTE_VERTEX, "vVertex",
GLT_ATTRIBUTE_NORMAL, "vNormal");
//對統(tǒng)1值的取值,得到對應(yīng)的統(tǒng)1值的位置
locColor = glGetUniformLocation(diffuseLightShader, "diffuseColor");
locLight = glGetUniformLocation(diffuseLightShader, "vLightPosition");
locMVP = glGetUniformLocation(diffuseLightShader, "mvpMatrix");
locMV = glGetUniformLocation(diffuseLightShader, "mvMatrix");
locNM = glGetUniformLocation(diffuseLightShader, "normalMatrix");
}
// Cleanup
void ShutdownRC(void)
{
}
// 任務(wù):硬編碼需要傳入渲染器的兩個(gè)值、激活shader程序、對對應(yīng)的統(tǒng)1值矩陣進(jìn)行賦值
void RenderScene(void)
{
static CStopWatch rotTimer;
// 清除當(dāng)前色彩緩沖區(qū)和深度緩沖區(qū)的數(shù)據(jù)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//對模型視圖矩陣壓入視圖幀
modelViewMatrix.PushMatrix(viewFrame);
modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds() * 10.0f, 0.0f, 1.0f, 0.0f);
//硬編碼需要傳入渲染器程序當(dāng)中的值
GLfloat vEyeLight[] = { -100.0f, 100.0f, 100.0f };
GLfloat vDiffuseColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };
//激活shader程序
glUseProgram(diffuseLightShader);
//對對應(yīng)的屬性進(jìn)行賦值
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);
//設(shè)置透視模式
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 100.0f);
//加載透視投影矩陣
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//渲染管線的設(shè)置矩陣堆棧,加載模型視圖矩陣和透視矩陣
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;
}
主程序,對漫反射渲染器的使用和操作。
注意1個(gè)常常出現(xiàn)的毛病:在設(shè)置著色器統(tǒng)1值以后,和對幾何圖形進(jìn)行渲染之前做進(jìn)1步修改。注意:glUniform函數(shù)其實(shí)不會(huì)將1個(gè)對這些數(shù)據(jù)的援用復(fù)制到著色器。這個(gè)函數(shù)會(huì)將實(shí)際數(shù)據(jù)復(fù)制到著色器中。
(3)ADS光照模型
ADS代表:Ambient環(huán)境光、Diffuse漫反射、Specular鏡面光。它遵守1個(gè)簡單的原則,物體有3種材質(zhì)屬性,環(huán)境光反射,漫反射,鏡面反射。這些屬性都是分配的色彩值,更明亮的色彩代表更高的反射量。光源也有這3種相同的屬性。這些屬性都是分配的色彩值,更明亮的色彩代表更高的反射量。光源也有這3種相同的屬性。一樣是分配的色彩值,表示光源的亮度。終究的頂點(diǎn)色彩就是光照和材質(zhì)的這3個(gè)屬性相互影響的總和。
環(huán)境光
環(huán)境光不來自任何特定的方向,來自某個(gè)光源,但是光線卻是在房間或場景當(dāng)中4處反射,沒有方向可言。
由于環(huán)境光所照耀的物體在所有方向的表面都是均勻照亮的。我們可以把環(huán)境光看成是利用到每一個(gè)光源的全局“照明”因子。這中光照份量確切非常接近環(huán)境中源自光源的散射光。
由環(huán)境光所照耀的物體在所有方向的表面都是均勻照亮的,我們可以把環(huán)境光看成利用到每一個(gè)光源的全局“照明”因子。這類光照份量確切非常接近環(huán)境中源自光源的散射光。
為了計(jì)算環(huán)境光源對終究頂點(diǎn)色彩的影響,環(huán)境光材質(zhì)的性質(zhì)由環(huán)境光的值來度量,這個(gè)值產(chǎn)生對環(huán)境色彩的影響。在GLSL著色器當(dāng)中,環(huán)境光的具體實(shí)現(xiàn)以下:
uniform vec3 vAmbientMaterial;
uniform vec3 vAmbientLight;
vec3 vAmbientColor = vAmbientMaterial * vAmbientLight;
漫射光
漫射光是光源的定向份量,也是我們前面的示例光照著色器的主題。在著色器當(dāng)中的代碼以下:
uniform vec3 vDiffuseMaterial;
uniform vec3 vDiffuseLight;
float fDotProduct = max(0.0, dot(vNormal, vLightDir));
vec3 vDiffuseColor = vDiffuseMaaterial * vDiffuseLight * fDotProduct;
鏡面光
鏡面光具有非常強(qiáng)的方向性,但是他的反射角度非常鋒利,僅僅沿著特定的方向反射。高強(qiáng)度的鏡面光趨向于在它所照耀的表面上構(gòu)成1個(gè)亮點(diǎn),成為鏡面亮點(diǎn)。由于高度方向性本質(zhì),根據(jù)視察者的位置不同,鏡面光乃至有可能看不到。聚光燈和太陽都是產(chǎn)生很強(qiáng)的鏡面光的例子,不過他們固然必須是照耀在1個(gè)光亮的物體。
首先我們必須找到被表面法線反射的向量和反向的光線向量。隨后這兩個(gè)向量的點(diǎn)乘積將會(huì)取“反光度”次冪。反光度越大,鏡面反射高光越小。
利用GLSL所實(shí)現(xiàn)的功能以下:
uniform vec3 vSpecularMaterial;
uniform vec3 vSpecularLight;
float shininess=128f;
vec3 vReflection= reflect(-vLightDir, vEyeNormal);
fSpec = pow(EyeReflectionAngle, shininess);
vec3 vSpecularColor= vSpecularLight*vSpecularMaaterial*fSpec;
注意,和其他參數(shù)1樣,反光度參數(shù)也能夠是統(tǒng)1值,最高的鏡面指數(shù)設(shè)置為128,大于這個(gè)數(shù)字,這個(gè)值將會(huì)逐步減弱。
注意,頂點(diǎn)終究的色彩可以像下面這樣進(jìn)行計(jì)算:
vVertexColor=vAmbientColor+vDiffuseColor+vSpecularColor;
// ADS Point lighting Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
// 輸入的數(shù)據(jù),依然是點(diǎn)坐標(biāo)和點(diǎn)的法向量
in vec4 vVertex;
in vec3 vNormal;
// 設(shè)置批次
uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;
uniform vec3 vLightPosition;
uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
// 終究傳遞給片元渲染器的color
smooth out vec4 vVaryingColor;
void main(void)
{
// 得到視覺坐標(biāo)系的法向量
vec3 vEyeNormal = normalMatrix * vNormal;
// 得到視覺坐標(biāo)系的坐標(biāo)
vec4 vPosition4 = mvMatrix * vVertex;
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
// 得到光源向量
vec3 vLightDir = normalize(vLightPosition - vPosition3);
// 點(diǎn)乘得到1個(gè)漫反射強(qiáng)度
float diff = max(0.0, dot(vEyeNormal, vLightDir));
// 強(qiáng)度乘以漫反射色彩
vVaryingColor = diff * diffuseColor;
// 在此基礎(chǔ)上加上環(huán)境光
vVaryingColor += ambientColor;
// 鏡面光
//得到光源的反射向量
vec3 vReflection = normalize(reflect(-vLightDir, vEyeNormal));
//得到反射角的cos值
float spec = max(0.0, dot(vEyeNormal, vReflection));
if(diff != 0) {
float fSpec = pow(spec, 128.0);
vVaryingColor.rgb += vec3(fSpec, fSpec, fSpec);
}
//終究利用幾何變換
gl_Position = mvpMatrix * vVertex;
}
ADS的頂點(diǎn)渲染器代碼如上。
#version 130
out vec4 vFragColor;
smooth in vec4 vVaryingColor;
void main(void)
{
vFragColor = vVaryingColor;
}
片元著色器如上。
(4)Phong著色
3角形之間的不連續(xù)會(huì)致使上面所渲染的球體會(huì)產(chǎn)生亮線星光的效果。這類不連續(xù)則是由于色彩值在空間中進(jìn)行的是線性插值的緣由。亮線就是兩個(gè)獨(dú)立3角形之間的縫隙。
解決這類效果的1種方法叫做Phong著色。我們不在頂點(diǎn)之間進(jìn)行色彩插值,而是在頂點(diǎn)之間進(jìn)行表面法線插值,這就是所謂的Phong著色。
Phong著色會(huì)致使片元著色器中作的工作大大提高,由于片斷著色器的履行次數(shù)將會(huì)比頂點(diǎn)程序的履行次數(shù)多很多。
// ADS Point lighting Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
//輸入每一個(gè)頂點(diǎn)和每一個(gè)頂點(diǎn)的法線
in vec4 vVertex;
in vec3 vNormal;
uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
uniform vec3 vLightPosition;
// 片斷程序色彩
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
void main(void)
{
// 獲得表面法線的視覺坐標(biāo)
vVaryingNormal = normalMatrix * vNormal;
// 獲得頂點(diǎn)位置的視覺坐標(biāo)
vec4 vPosition4 = mvMatrix * vVertex;
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
// 獲得到光源的方向
vVaryingLightDir = normalize(vLightPosition - vPosition3);
// 最后對多邊形進(jìn)行變換
gl_Position = mvpMatrix * vVertex;
}
Phong頂點(diǎn)著色器,把具體的操作幾近都轉(zhuǎn)移到片元著色器上了。
#version 130
out vec4 vFragColor;
uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
void main(void)
{
// 點(diǎn)乘得到漫反射的強(qiáng)度
float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));
// 用強(qiáng)度去乘以漫反射色彩將色彩設(shè)置為1.0
vFragColor = diff * diffuseColor;
// 增加環(huán)境光
vFragColor += ambientColor;
// 鏡面光
vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal)));
float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection));
if(diff != 0) {
float fSpec = pow(spec, 128.0);
vFragColor.rgb += vec3(fSpec, fSpec, fSpec);
}
}
如上所示為片元著色器,大量的操作從頂點(diǎn)著色器轉(zhuǎn)移到片元著色器當(dāng)中,但是代碼幾近完全沒有改變,僅僅轉(zhuǎn)移了計(jì)算位置。這樣算是高質(zhì)量渲染了,會(huì)影響性能。
注意:1個(gè)著色器性能優(yōu)化的常規(guī)原則:將盡量多的處理進(jìn)程移出片元著色器而放入頂點(diǎn)著色器。
下一篇 Xcode8證書錯(cuò)誤