目前我们所展示的阴影生成方法都仅限于生成硬阴影,即带锐边的阴影。但是,现实世
界中出现的大多数阴影都是柔和阴影。它们的边缘都会发生不同程度的模糊。在本节中,
我们将探讨现实世界中柔和阴影的外观,然后描述在OpenGL 中模拟它们的常用算法。消
除锯齿状阴影边缘并不像处理之前的伪影那么简单。
柔和阴影的成因有很多,同时也有许多类型的柔和阴影。通常在自然界中产生柔和阴影
原因是,真实世界的光源很少是点光源——它们常常是区域光源。另一个原因是材料和表面
的缺陷积累,以及物体本身通过其自身的反射特性产生环境光的作用。
图8.16 展示了物体向桌面投射柔和阴影的照片示例。注意,这不是计算机渲染的3D 场
景,而是真实的照片,是本书作者之一在家中拍摄的。
对于图8.16 中的阴影,有两点需要注意。
离物体越远的阴影越“柔和”,离物体越近的阴影越“硬”。在对比物体腿附近的阴
影与右边更宽的阴影时,这一点就很明显了。
距离物体越近的阴影显得越暗。
光源本身的维度会导致柔和阴影。如图1所示,光源上各处会投射出略微不同的阴影。
各种阴影不同的区域称为半影(penumbra),包括阴影边缘的柔和区域。
图1
有多种方法可以用来模拟半影效果以在软件中生成柔和阴影。最简单也最常见的一种方
法叫作百分比邻近滤波(Percentage Closer Filtering,PCF)。在PCF 中,我们对单个点周围
的几个位置的阴影纹理进行采样,以估计附近位置在阴影中的百分比。根据附近位置在阴
影中的数量,对正在渲染的像素的光照分量进行修改。整个计算可以在片段着色器中完成,
所以我们只需要对其中的代码进行修改。PCF 还可用于减少锯齿线伪影。
在研究实际的PCF 算法之前,我们先看一个类似的简单示例来展示PCF 的目标。考虑图2 中所示
的输出片段(像素)集,其颜色由片段着色器计算。
假设深色像素处于阴影中,这是阴影贴图计算的
结果。假设我们可以访问相邻的像素信息,而不是简
单地如图所示渲染像素(即包括或不包括漫反射和镜
面反射分量),这样我们就可以看到有多少相邻像素
处于阴影中。例如,考虑图图3(见彩插)中以黄色
突出显示的特定像素,根据图图3,该像素不在阴影
中。
图2
图3
在高亮像素的9 个像素邻域中,3 个像素处于阴影中而6 个像素处于阴影外。因此,渲染
像素的颜色可以被计算为该像素处的环境光分量加上漫反射和镜面反射分量的9/6,这样会使像素一定程度(但不是完全)变亮。在整个网格中重复此过程将会产生图图4所示的像素颜色。注意,对于那些邻域完全
位于阴影中(或阴影外)的像素,生成的颜色与标准阴
影贴图相同。
与上例不同的是,在PCF 的实现中,不是对渲染
像素临近区域内的每个像素进行采样。这有两个原因:
(a)我们想在片段着色器中执行此计算,但片段着色
器无法访问其他像素;(b)获得足够宽的半影效果(例
如,10~20 像素宽)将需要为每个被渲染的像素采样数百个附近的像素。
PCF 解决了以下两个问题。首先,我们不试图访问附近的像素,而是在阴影贴图中对附
近的纹素进行采样。片段着色器可以执行此操作,因为虽然它无法访问附近像素的值,但
它可以访问整个阴影贴图。其次,为了获得足够宽的半影效果,需要对附近一定数量的阴
影贴图纹素进行采样,每个采样的纹素都距离所渲染像素的纹素一定距离。
半影的宽度和采样点数可以根据场景和性能要求调整。例如,图8.21 所示PCF 生成的
图像是,每个像素的亮度是通过对64 个不同的纹素进行采样确定的,它们与像素的纹素距
离各不相同。
柔和阴影的准确度或平滑度取决于所采样附
近纹素的数量。因此,在性能和质量之间需要权
衡——采样点越多,效果越好,但计算开销也越多。
场景的复杂性和给定应用所需的帧率对于阴影可
实现的质量有着相应的限制。每像素采样64 个点
(如图4 所示)通常是不切实际的。
一种用于实现PCF 的常见算法是对每个像素
附近的4 个纹素进行采样,其中样本通过指定从像
素对应纹素的偏移量选择。对于每个像素,我们都
需要改变偏移量,并用新的偏移量确定采样的4 个
纹素。用交错方式改变偏移量的方法被称为抖动,它旨在使得柔和阴影边界不会由于采样
点不足看起来“结块”。
一种常见的方法是假设有4 种不同偏移模式,每次取其中一种——我们可以通过计算像
素的glFragCoord mod 2 来选择当前像素的偏移模式。之前有提到,glFragCoord 是vec2 类
型,包含像素位置的x、y 坐标。因此,mod 计算的结果有4 种可能的值:(0,0)、(0,1)、(1,0)
或(1,1)。我们使用glFragCoord mod 2 的结果来从纹素空间(即阴影贴图)4 种不同偏移模
式中选择一种。
偏移模式通常在x 和y 方向上指定,具有−1.5,−0.5,+0.5 和+1.5 的不同组合(也可以
根据需要进行缩放)。更具体来说,由glFragCoord mod 2 计算得到的每种情况的4 种常用偏
移模式是:
图4 柔和阴影渲染——
每像素64 次采样
图8.20 柔和阴影渲染
图4
Sx 和Sy 指的是与正在渲染的像素相对应的阴影贴图中的位置(Sx,Sy),在本章的代码示
例中标识为shadow_coord。这4 种偏移模式如图8.22 所示(见彩插),每种情况都以不同的
颜色显示。在每种情况下,对应于正被渲染的像素的纹素位于该情况的图的原点。请注意,
当在图8.23(见彩插)中一起显示时,偏移的交错/抖动很明显。
图5
让我们来针对特定像素看看整个计算过程。假设正在渲染的像素位于glFragCoord =
(48,13)。首先我们确定像素在阴影贴图的4 个采样点。为此,我们将计算vec2(48,13) mod 2,
等于(0,1)。因此我们选择(0,1)所对应的偏移,在图8.22 中以绿色显示,并且在阴影贴
图对相应的点进行采样(假设没有指定偏移的缩放量),得到:
(shadow_coord.x–1.5, shadow_coord.y+0.5)
(shadow_coord.x–1.5, shadow_coord.y–1.5)
(shadow_coord.x+0.5, shadow_coord.y+0.5)
(shadow_coord.x+0.5, shadow_coord.y–1.5)
(回想一下,shadow_coord 是阴影贴图中与正在渲染的像素相对应的纹素的位置——在
图6中显示为白色圆圈)。
接下来,对我们选取的这4 个点分别调用textureProj(),在每种情况下都返回0.0 或1.0,
具体取决于该采样点是否在阴影中。将4 个结果相加并除以4.0,就可以确定阴影中采样
点的百分比。然后将此百分比用作乘数,确定渲染当前像素时要应用的漫反射和镜面反射
分量。
尽管采样尺寸很小——每个像素只有4 个样本——这种抖动方法通常可以产生好得惊人
的柔和阴影。图8.24 是使用4 像素抖动PCF 生成的。虽然它不如之前图8.21 所示的64 点
采样版本好,但渲染速度要快得多。
在下一节中,我们对GLSL 片段着色器进行编码,实现4 采样抖动的PCF 柔和阴影以
及之前展示的64 采样PCF 柔和阴影。
#include "glew/glew.h"
#include "glfw/glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "SOIL2/SOIL2.h"
#include "Torus.h"
#include "ImportedModel.h"
#include "Utils.h"
#include
#include
#include
#include
void passOne(void);
void passTwo(void);
static const float pai = 3.1415926f;
float toRadins(float degrees) { return (degrees * 2.f * pai) / float(360.f); }
static const int numVAOs = 1;
static const int numVBOs = 5;
GLuint vao[numVAOs] = { 0 };
GLuint vbo[numVBOs] = { 0 };
GLuint renderingProgram1 = 0, renderingProgram2 = 0;
ImportedModel pyramid("pyr.obj");
Torus myTorus(0.6f, 0.4f, 48);
int numPyramidVertices = 0, numTorusVertices = 0, numTorusIndices = 0;
glm::vec3 torusLoc(1.6f, 0.f, -0.3f);
//glm::vec3 torusLoc(0.0f, 0.0f, -0.0f);
glm::vec3 pyramidLoc(-1.f, 0.1f, 0.3f);
//glm::vec3 pyramidLoc(0.0f, 0.0f, 0.0f);
glm::vec3 cameraLoc(0.f, 0.2f, 6.f);
glm::vec3 lightLoc(-3.8f, 2.2f, 1.1f);
// white light
float globalAmbient[4] = { 0.7f, 0.7f, 0.7f, 1.f };
float lightAmbient[4] = { 0.f, 0.f, 0.f, 1.f };
float lightDiffuse[4] = { 1.f };
float lightSpecular[4] = { 1.f };
// gold material
float* gMatAmb = Utils::goldAmbient();
float* gMatDiff = Utils::goldDiffuse();
float* gMatSpec = Utils::goldSpecular();
float gMatShin = Utils::goldShininess();
// bronze material
float* bMatAmb = Utils::bronzeAmbient();
float* bMatDiff = Utils::bronzeDiffuse();
float* bMatSpec = Utils::bronzeSpecular();
float bMatShin = Utils::bronzeShininess();
float thisAmb[4] = { 0.f }, thisDif[4] = { 0.f }, thisSpe[4] = { 0.f }, matAmb[4] = { 0.f }, matDif[4] = { 0.f }, matSpe[4] = { 0.f };
float thisShin = 0.f, matShin = 0.f;
// shadow stuff
int scSizeX = 0, scSizeY = 0;
GLuint shadowTex = 0, shadowBuffer = 0;
glm::mat4 lightVMatrix(1.f);
glm::mat4 lightPMatrix(1.f);
glm::mat4 shadowMVP1(1.f);
glm::mat4 shadowMVP2(1.f);
glm::mat4 b(1.f);
// variable allocation for display
GLuint mvLoc = 0, projLoc = 0, nLoc = 0, sLoc = 0;
static const int screenWidth = 1920;
static const int screenHeight = 1080;
int width = 0, height = 0;
float aspect = 0.f;
glm::mat4 mMat(1.f), vMat(1.f), pMat(1.f), mvMat(1.f), invTrMat(1.f);
glm::vec3 currentLightPos(0.f), transformed(0.f);
float lightPos[3] = { 0.f };
GLuint globalAmbLoc = 0, ambLoc = 0, diffLoc = 0, specLoc = 0, posLoc = 0, mAmbLoc = 0, mDiffLoc = 0, mSpecLoc = 0, mShinLoc = 0;
glm::vec3 origin(0.f);
glm::vec3 up(0.f, 1.f, 0.f);
void installLights(int renderingProgram, glm::mat4 vMatrix)
{
transformed = glm::vec3(vMatrix * glm::vec4(currentLightPos, 1.f));
lightPos[0] = transformed.x;
lightPos[1] = transformed.y;
lightPos[2] = transformed.z;
matAmb[0] = thisAmb[0]; matAmb[1] = thisAmb[1]; matAmb[2] = thisAmb[2]; matAmb[3] = thisAmb[3];
matDif[0] = thisDif[0]; matDif[1] = thisDif[1]; matDif[2] = thisDif[2]; matDif[3] = thisDif[3];
matSpe[0] = thisSpe[0]; matSpe[1] = thisSpe[1]; matSpe[2] = thisSpe[2]; matSpe[3] = thisSpe[3];
matShin = thisShin;
// get the locations of the light and material fields in the shader
globalAmbLoc = glGetUniformLocation(renderingProgram, "globalAmbient");
ambLoc = glGetUniformLocation(renderingProgram, "light.ambient");
diffLoc = glGetUniformLocation(renderingProgram, "light.diffuse");
specLoc = glGetUniformLocation(renderingProgram, "light.specular");
posLoc = glGetUniformLocation(renderingProgram, "light.position");
mAmbLoc = glGetUniformLocation(renderingProgram, "material.ambient");
mDiffLoc = glGetUniformLocation(renderingProgram, "material.diffuse");
mSpecLoc = glGetUniformLocation(renderingProgram, "material.specular");
mShinLoc = glGetUniformLocation(renderingProgram, "material.shininess");
// set the uniform light and material values in the shader
glProgramUniform4fv(renderingProgram, globalAmbLoc, 1, globalAmbient);
glProgramUniform4fv(renderingProgram, ambLoc, 1, lightAmbient);
glProgramUniform4fv(renderingProgram, diffLoc, 1, lightDiffuse);
glProgramUniform4fv(renderingProgram, specLoc, 1, lightSpecular);
glProgramUniform3fv(renderingProgram, posLoc, 1, lightPos);
glProgramUniform4fv(renderingProgram, mAmbLoc, 1, matAmb);
glProgramUniform4fv(renderingProgram, mDiffLoc, 1, matDif);
glProgramUniform4fv(renderingProgram, mSpecLoc, 1, matSpe);
glProgramUniform1f(renderingProgram, mShinLoc, matShin);
}
void setupVertices(void)
{
numPyramidVertices = pyramid.getNumVertices();
std::vector<glm::vec3> vert = pyramid.getVertices();
std::vector<glm::vec3> norm = pyramid.getNormals();
std::vector<float> pyramidPValues;
std::vector<float> pyramidNValues;
for (int i = 0; i < numPyramidVertices; i++)
{
pyramidPValues.push_back((vert[i]).x);
pyramidPValues.push_back((vert[i]).y);
pyramidPValues.push_back((vert[i]).z);
pyramidNValues.push_back((norm[i]).x);
pyramidNValues.push_back((norm[i]).y);
pyramidNValues.push_back((norm[i]).z);
}
// torus definition
numTorusVertices = myTorus.getNumVertices();
numTorusIndices = myTorus.getNumIndices();
std::vector<int> ind = myTorus.getIndices();
//清空容器
vert.clear();
norm.clear();
vert = myTorus.getVertices();
norm = myTorus.getNormals();
std::vector<float> torusPValues;
std::vector<float> torusNValues;
for (int i = 0; i < numTorusVertices; i++)
{
torusPValues.push_back((vert[i]).x);
torusPValues.push_back((vert[i]).y);
torusPValues.push_back((vert[i]).z);
torusNValues.push_back((norm[i]).x);
torusNValues.push_back((norm[i]).y);
torusNValues.push_back((norm[i]).z);
}
glGenVertexArrays(numVAOs, vao);
glBindVertexArray(vao[0]);
glGenBuffers(numVBOs, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, torusPValues.size() * sizeof(float), &torusPValues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, pyramidPValues.size() * sizeof(float), &pyramidPValues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, torusNValues.size() * sizeof(float), &torusNValues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[3]);
glBufferData(GL_ARRAY_BUFFER, pyramidNValues.size() * sizeof(int), &pyramidNValues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[4]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size() * sizeof(int), &ind[0], GL_STATIC_DRAW);
}
void setupShadowBuffer(GLFWwindow* window)
{
glfwGetFramebufferSize(window, &width, &height);
scSizeX = width;
scSizeY = height;
glGenFramebuffers(1, &shadowBuffer);
glGenTextures(1, &shadowTex);
glBindTexture(GL_TEXTURE_2D, shadowTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, scSizeX, scSizeY, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
// may reduce shadow border artifacts 可以减少阴影边界伪影
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
void init(GLFWwindow* window)
{
renderingProgram1 = Utils::createShaderProgram("vert1Shader.vert", "frag1Shader.frag");
renderingProgram2 = Utils::createShaderProgram("vert2Shader.vert", "frag2Shader.frag");
glfwGetFramebufferSize(window, &width, &height);
aspect = (float)width / (float)height;
pMat = glm::perspective(glm::radians(45.f), aspect, 0.001f, 1000.f);
setupVertices();
setupShadowBuffer(window);
b = glm::mat4
(
0.5f, 0.f, 0.f, 0.f,
0.f, 0.5f, 0.f, 0.f,
0.f, 0.f, 0.5f, 0.f,
0.5f, 0.5f, 0.5f, 1.f
);
}
void display(GLFWwindow* window, double currentTime)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.f, 0.5f, 1.f, 1.f);
currentLightPos = glm::vec3(lightLoc);
lightVMatrix = glm::lookAt(currentLightPos, origin, up);
lightPMatrix = glm::perspective(glm::radians(60.f), aspect, 0.001f, 1000.f);
glBindFramebuffer(GL_FRAMEBUFFER, shadowBuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowTex, 0);
//不写入颜色缓冲区,如果存在片段着色器则不启用该着色器
glDrawBuffer(GL_NONE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_POLYGON_OFFSET_FILL); //为减少伪影
glPolygonOffset(2.f, 4.f);
passOne();
glDisable(GL_POLYGON_OFFSET_FILL); //为减少伪影
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, shadowTex);
glDrawBuffer(GL_FRONT);
passTwo();
}
void passOne(void)
{
glUseProgram(renderingProgram1);
// draw the torus
mMat = glm::translate(glm::mat4(1.f), torusLoc);
mMat = glm::rotate(mMat, glm::radians(25.f), glm::vec3(1.f, 0.f, 0.f));
shadowMVP1 = lightPMatrix * lightVMatrix * mMat;
sLoc = glGetUniformLocation(renderingProgram1, "shadowMVP");
glUniformMatrix4fv(sLoc, 1, GL_FALSE, glm::value_ptr(shadowMVP1));
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[4]);
glDrawElements(GL_TRIANGLES, numTorusIndices, GL_UNSIGNED_INT, 0);
// draw the pyramid
mMat = glm::translate(glm::mat4(1.f), pyramidLoc);
mMat = glm::rotate(mMat, glm::radians(30.f), glm::vec3(1.f, 0.f, 0.f));
mMat = glm::rotate(mMat, glm::radians(40.f), glm::vec3(0.f, 1.f, 0.f));
shadowMVP1 = lightPMatrix * lightVMatrix * mMat;
glUniformMatrix4fv(sLoc, 1, GL_FALSE, glm::value_ptr(shadowMVP1));
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
//glEnableVertexAttribArray(0);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDrawArrays(GL_TRIANGLES, 0, numPyramidVertices);
}
void passTwo()
{
glUseProgram(renderingProgram2);
mvLoc = glGetUniformLocation(renderingProgram2, "mv_matrix");
projLoc = glGetUniformLocation(renderingProgram2, "proj_matrix");
nLoc = glGetUniformLocation(renderingProgram2, "norm_matrix");
sLoc = glGetUniformLocation(renderingProgram2, "shadowMVP");
// draw the torus
thisAmb[0] = bMatAmb[0]; thisAmb[1] = bMatAmb[1]; thisAmb[2] = bMatAmb[2]; // bronze
thisDif[0] = bMatDiff[0]; thisDif[1] = bMatDiff[1]; thisDif[2] = bMatDiff[2];
thisSpe[0] = bMatSpec[0]; thisSpe[1] = bMatSpec[1]; thisSpe[2] = bMatSpec[2];
thisShin = bMatShin;
vMat = glm::translate(glm::mat4(1.f), glm::vec3(-cameraLoc.x, -cameraLoc.y, -cameraLoc.z));
mMat = glm::translate(glm::mat4(1.f), torusLoc);
mMat = glm::rotate(mMat, glm::radians(25.f), glm::vec3(1.f, 0.f, 0.f));
currentLightPos = glm::vec3(lightLoc);
installLights(renderingProgram2, vMat);
mvMat = vMat * mMat;
invTrMat = glm::transpose(glm::inverse(mvMat));
shadowMVP2 = b * lightPMatrix * lightVMatrix * mMat;
glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
glUniformMatrix4fv(nLoc, 1, GL_FALSE, glm::value_ptr(invTrMat));
glUniformMatrix4fv(sLoc, 1, GL_FALSE, glm::value_ptr(shadowMVP2));
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glFrontFace(GL_CCW);
glDepthFunc(GL_LEQUAL);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[4]);
glDrawElements(GL_TRIANGLES, numTorusIndices, GL_UNSIGNED_INT, 0);
// draw the pyramid
thisAmb[0] = gMatAmb[0]; thisAmb[1] = gMatAmb[1]; thisAmb[2] = gMatAmb[2]; // gold
thisDif[0] = gMatDiff[0]; thisDif[1] = gMatDiff[1]; thisDif[2] = gMatDiff[2];
thisSpe[0] = gMatSpec[0]; thisSpe[1] = gMatSpec[1]; thisSpe[2] = gMatSpec[2];
thisShin = gMatShin;
mMat = glm::translate(glm::mat4(1.f), pyramidLoc);
mMat = glm::rotate(mMat, glm::radians(40.f), glm::vec3(1.f, 0.f, 0.f));
mMat = glm::rotate(mMat, glm::radians(30.f), glm::vec3(0.f, 1.f, 0.f));
currentLightPos = glm::vec3(lightLoc);
installLights(renderingProgram2, vMat);
mvMat = vMat * mMat;
invTrMat = glm::transpose(glm::inverse(mvMat));
shadowMVP2 = b * lightPMatrix * lightVMatrix * mMat;
glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
glUniformMatrix4fv(nLoc, 1, GL_FALSE, glm::value_ptr(invTrMat));
glUniformMatrix4fv(sLoc, 1, GL_FALSE, glm::value_ptr(shadowMVP2));
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[3]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDrawArrays(GL_TRIANGLES, 0, numPyramidVertices);
}
void window_size_callback(GLFWwindow* window, int newWidth, int newHeight)
{
aspect = (float)newWidth / (float)newHeight;
glViewport(0, 0, newWidth, newHeight);
pMat = glm::perspective(glm::radians(45.f), aspect, 0.001f, 1000.f);
}
int main(int argc, char** argv)
{
int glfwState = glfwInit();
if (GLFW_FALSE == glfwState)
{
std::cout << "GLFW initialize failed,invoke glfwInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << std::endl;
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_PROFILE);
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "shadow mapping soft PCF", nullptr, nullptr);
if (!window)
{
std::cout << "GLFW create window failed,invoke glfwCreateWindow()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << std::endl;
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
glfwSetWindowSizeCallback(window, window_size_callback);
int glewState = glewInit();
if (GLEW_OK != glewState)
{
std::cout << "GLEW initialize failed,invoke glewInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << std::endl;
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwSwapInterval(1);
init(window);
while (!glfwWindowShouldClose(window))
{
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
return 0;
}
工程源码下载地址