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