这里不介绍算法原理,只说说在实现过程中遇到的问题,以及背后的原因。开发环境:opengl 2.0  glsl 1.0。

第一个问题:产生深度纹理。

在opengl中每一次离屏渲染需要向opengl提供一个renderframe,一个renderframe包含一个texture和一个renderbuffer.texture是一个存储特定数据的内存区,可以存储颜色,深度以及模版。renderbuffer目前不太清楚。

具体代码如下:

    glGenFramebuffers(, &frameBuff) ;
glBindFramebuffer(GL_FRAMEBUFFER, frameBuff) ; glGenTextures(, &depthTxe) ;
glBindTexture(GL_TEXTURE_2D, depthTxe) ;
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, , GL_DEPTH_COMPONENT16 , mapWidth, mapHight, , GL_DEPTH_COMPONENT, GL_FLOAT, );
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTxe, );
glDrawBuffer(GL_NONE) ;
glReadBuffer(GL_NONE) ;
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if( result == GL_FRAMEBUFFER_COMPLETE) {
cout << "Framebuffer is complete.\n" << endl ;
} else {
cout <<"Framebuffer is not complete.\n" << endl ;
}

这里需要注意的是,一定要加    glDrawBuffer(GL_NONE) ;  glReadBuffer(GL_NONE) 。分别告诉opengl没有缓冲区接受或者读取颜色数据。因为我们需要的是深度数据。如果没有这两句,那么返回的result是not complete。

曾经尝试过使用 glTexImage2D 中使用 GL_RGB 而不是 GL_DEPTH_COMPONENT16 相应的下面的 glFramebufferTexture2D 也要使用 GL_COLOR_ATTACHMENT。在fragment shader中将片元的深度值写入color中。但是这样有个问题,就是精度不够,虽然数据是对的,精度在Shadow Map中有着极其重要的位置,稍后介绍。

问题二:各种坐标系的转化

这里涉及到两个变化过程。第一个是从物体坐标系-》世界坐标系-》灯光坐标系-》裁剪坐标系(齐次坐标系)-》cvv坐标系(透视除法后得到)。

另一个是物体坐标系-》世界坐标系-》摄像机坐标系-》裁剪坐标系(齐次坐标系)-》cvv坐标系(透视除法后得到)。

具体vertex shader 如下

varying vec3 normal ;
varying vec4 lightVertex ;
varying vec4 color ;
varying vec4 worldCoord ;
uniform mat4 lightProj;
uniform mat4 lightView;
const mat4 biasMatrix = mat4(0.5 , 0.0 , 0.0 , 0.0 ,
0.0 , 0.5 , 0.0 , 0.0 ,
0.0 , 0.0 , 0.5 , 0.0 ,
0.5 , 0.5 , 0.5 , 1.0 ) ;
void main()
{
worldCoord = gl_ModelViewMatrix * gl_Vertex ;
normal = normalize(gl_NormalMatrix * gl_Normal);
lightVertex = lightProj * lightView * worldCoord ;
lightVertex = lightVertex / lightVertex.w ;
lightVertex = biasMatrix * lightVertex ;
//lightVertex = lightVertex / lightVertex.w ;
gl_TexCoord[] = gl_MultiTexCoord0 ;
color = gl_Color ;
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex ;
}

这里需要注意的是glsl中没有提供从物体坐标系到世界坐标系的转化,gl_ModelViewMatrix实现的是从物体到相机坐标系的转化,在本次shadow map是实现过程中将一直保证摄像机在世界坐标系的原点位置,方向指向(0,0,-1),向上方向为(0,1,0)。也就是说摄像机和世界坐标系重合。

注意到 biasMatrix,这里可以推知两点:

(1)glsl中是采用列主序的方式存放矩阵的,也就是数据先放完第一列再放第二列。

(2)glsl纹理的坐标被映射到[0,1]之间。用本次shadow map举例,从灯光坐标系到齐次坐标系,经过裁剪以及透视除法后,得到的x,y,z的范围是在[-1,1]之间的,构成一个规范立方体(cvv),那么x , y的值就是纹理在纹理内存中的位置,z就是伪深度,用于z-buffer。但是,opengl在渲染深度纹理时,将x,y,z映射到[0,1]之间,所以要用一个biasMatrix进行平移。

第三个问题:Shadow acne。

这个问题的原因如下图:

其中,斜线EF是物体表面,当一光柱照射到物体表面时,C点的深度值被写入shadow map中(这里有一个概念:texle , texture element,可以理解为一个像素)。但是,当从摄像机坐标系中看到D点时,通过坐标系转化到shadow map中 ,D点比C点的值远离光源。这样就造成了shadow acne。效果如下:

这时要加上一个偏移两bias,具体计算在fragment shader中,其中的m时具体情况具体调整的,时经验值。

问题四:阴影有锯齿。

如下图:

改进方法时使用多次采样,可以使用采样次sampler2Dshadow,内部会采样一个像素周围的几个像素。也可以使用本次代码中的poissonDisk进行优化。效果如下

这个poissonDisk(柏松盘采样)内容目前不太了解,貌似效果还不错。

学习心得:在碰到一个新技术时,最好把其最简单的部分实现,然后看看有什么问题,然后慢慢优化。如果不动手实现,很难体会资料上描述的问题的现象,更不用说问题产生的原因。

一下时全部代码:

#include <GLUT/GLUT.h>
#include <iostream>
#include "LoardShaderProgram.h"
#include "BW_READBMP.h"
using namespace std ; struct COLOR
{
float r , g , b, w ;
} ;
float rot = 0.1 ;
float transX = ;
float transY = ;
float transZ = ;
GLint program ;
GLint lightPosLoc ;
GLint lightAmbientLoc ;
GLint lightLambertLoc ;
GLint lightProjLoc ;
GLint lightViewLoc ;
GLint mLoc ;
GLint ksLoc ;
GLint kdLoc ;
GLint kaLoc ;
GLint texLoc ; GLint tex ;
GLint mapWidth = ;
GLint mapHight = ;
GLuint depthTxe ;
GLuint frameBuff ;
GLfloat lightProj[] ;
GLfloat lightView[] ;
float alf = ;
float ks = ;
float kd = 0.5 ;
float ka = 1.0 ;
float m = 0.0035410088 ;
COLOR lightPos ;
void DrawSence()
{
glTranslated(, , -) ;
glBegin(GL_QUADS);
glColor3f(, , ) ; // ground
//glNormal3f( 0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f);
glNormal3f(, , ) ;
glVertex3f(4.0f, -2.0f, -4.0f);
glNormal3f(, , ) ;
glTexCoord2f(0.0f, 1.0f);
glVertex3f( -4.0f, -2.0f, -4.0f);
glNormal3f(, , ) ;
glTexCoord2f(1.0f, 1.0f);
glVertex3f( -4.0f, -2.0f, 4.0f);
glNormal3f(, , ) ;
glTexCoord2f(1.0f, 0.0f);
glVertex3f(4.0f, -2.0f, 4.0f); glEnd() ; glTranslated(transX, transY , transZ) ;
glRotatef(rot,1.0,0.0,1.0);
glutSolidTeapot();
}
void ProcessKeyboard(unsigned char key,int x,int y)
{
if (key == 'x' || key == 'X')
{
rot += 0.8;
}
if (key == 'c' || key == 'C')
{
m += 0.00001 ;
}
if (key == 'd' || key == 'D')
{
m -= 0.00001 ;
}
if (key == 'u' || key == 'U')
{
transX += 0.3 ;
}
if (key == 'i' || key == 'I')
{
transX -= 0.3 ;
}
if (key == 'j' || key == 'J')
{
transY += 0.3 ;
}
if (key == 'k' || key == 'K')
{
transY -= 0.3 ;
}
if (key == 'o' || key == 'O')
{
transZ += 0.3 ;
}
if (key == 'p' || key == 'P')
{
transZ -= 0.3 ;
} glutPostRedisplay() ;
} void ReSizeScene(int width , int height)
{
if( height== )
{
height=;
}//if //设置视口
glViewport(,,width,height);
//设置透视矩阵
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
//开始对模型矩阵进行操作
glMatrixMode(GL_MODELVIEW);
//复位
glLoadIdentity();
}
void GenDepthMap()
{
lightPos.r = ; lightPos.g = ; lightPos.b = -; lightPos.w = ;
glBindFramebuffer(GL_FRAMEBUFFER, frameBuff) ;
glDrawBuffer(GL_NONE) ;
glViewport(, , mapWidth, mapHight) ;
glMatrixMode(GL_PROJECTION) ;
glLoadIdentity() ;
gluPerspective(, 4.0/, 0.1, ) ;
glGetFloatv(GL_PROJECTION_MATRIX, lightProj) ;
glMatrixMode(GL_MODELVIEW) ;
glLoadIdentity() ;
gluLookAt(lightPos.r, lightPos.g, lightPos.b, , , -, , , ) ;
glGetFloatv(GL_MODELVIEW_MATRIX,lightView) ;
glDisable(GL_TEXTURE_2D) ;
// glUseProgram(shadowPro) ;
glClearColor(1.0, 1.0, 1.0, 1.0 );
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) ;
DrawSence() ;
glUseProgram() ;
glBindFramebuffer(GL_FRAMEBUFFER, ) ;
glDrawBuffer(GL_FRONT) ;
}
void ShowSence()
{
ReSizeScene(, ) ;
COLOR lightAmbient , lightLambert ;
COLOR CameralDir , CameralPos;
lightAmbient.r = 0.1;lightAmbient.g = lightAmbient.b = 0.1; lightAmbient.w = ;
lightLambert.r = ; lightLambert.g = lightLambert.b = ;lightLambert.w = ; CameralDir.r = ;
CameralDir.g = ;
CameralDir.b = - ;
CameralDir.w = ; CameralPos.r = ;
CameralPos.g = ;
CameralPos.b = ;
CameralPos.w = ;
ka = ;
kd = 0.7 ;
ks = 0.4 ;
tex = ;
glActiveTexture(GL_TEXTURE0) ;
glBindTexture(GL_TEXTURE_2D, depthTxe) ;
glUseProgram(program) ;
glUniform4f(lightPosLoc, lightPos.r, lightPos.g, lightPos.b, lightPos.w) ;
glUniform4f(lightAmbientLoc, lightAmbient.r, lightAmbient.g, lightAmbient.b, lightAmbient.w) ;
glUniform4f(lightLambertLoc, lightLambert.r, lightLambert.g, lightLambert.b, lightLambert.w) ;
glUniformMatrix4fv(lightProjLoc, , GL_FALSE, lightProj) ;
glUniformMatrix4fv(lightViewLoc, , GL_FALSE, lightView) ;
glUniform1f(mLoc, m) ;
glUniform1f(kdLoc, kd) ;
glUniform1f(ksLoc, ks) ;
glUniform1f(kaLoc, ka) ;
glUniform1i(texLoc , tex) ;
gluLookAt(CameralPos.r, CameralPos.g , CameralPos.b, CameralDir.r, CameralDir.g , CameralDir.b, , , ) ;
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) ;
DrawSence() ;
glUseProgram() ;
glFlush();
}
void display()
{
GenDepthMap() ;
ShowSence() ;
} void InitScene()
{
glClearColor(1.0, 1.0, 0.0, 1.0) ;
glGenFramebuffers(, &frameBuff) ;
glBindFramebuffer(GL_FRAMEBUFFER, frameBuff) ; glGenTextures(, &depthTxe) ;
glBindTexture(GL_TEXTURE_2D, depthTxe) ;
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, , GL_DEPTH_COMPONENT16 , mapWidth, mapHight, , GL_DEPTH_COMPONENT, GL_FLOAT, );
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTxe, );
glDrawBuffer(GL_NONE) ;
glReadBuffer(GL_NONE) ;
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (result == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)
{
cout << "frambuffer GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER " << endl ;
} if( result == GL_FRAMEBUFFER_COMPLETE) {
cout << "Framebuffer is complete.\n" << endl ;
} else {
cout <<"Framebuffer is not complete.\n" << endl ;
} glBindFramebuffer(GL_FRAMEBUFFER, ) ;
glBindTexture(GL_TEXTURE_2D, ) ; SHADERINFO shaderInfo[] ;
shaderInfo[].shader_type = GL_VERTEX_SHADER ;
shaderInfo[].name = "ShadowMap.vertex" ;
shaderInfo[].shader_type = GL_FRAGMENT_SHADER ;
shaderInfo[].name = "ShadowMap.frag" ;
program = BWLoadShaders(shaderInfo, ) ;
lightPosLoc = glGetUniformLocation(program, "lightPos") ;
lightAmbientLoc = glGetUniformLocation(program, "lightAmbient") ;
lightLambertLoc = glGetUniformLocation(program, "lightLambert") ;
lightProjLoc = glGetUniformLocation(program, "lightProj") ;
lightViewLoc = glGetUniformLocation(program, "lightView") ;
mLoc = glGetUniformLocation(program, "m") ;
kdLoc = glGetUniformLocation(program, "kd") ;
ksLoc = glGetUniformLocation(program, "ks") ;
kaLoc = glGetUniformLocation(program, "ka") ;
texLoc = glGetUniformLocation(program, "tex") ;
glEnable(GL_DEPTH_TEST) ;
glDepthFunc(GL_LEQUAL);
glShadeModel(GL_FLAT) ; } int main(int argc, char ** argv)
{
glutInit(&argc, argv);
glutInitWindowSize(, );
glutInitDisplayMode(GLUT_RGB|GLUT_DEPTH) ;
glutCreateWindow("SHADERTEST");
glutKeyboardFunc(&ProcessKeyboard) ;
InitScene() ;
glutDisplayFunc(display);
glutMainLoop();
}

ShadowMap.vertex

varying vec3 normal ;
varying vec4 lightVertex ;
varying vec4 color ;
varying vec4 worldCoord ;
uniform mat4 lightProj;
uniform mat4 lightView;
uniform vec4 lightPos ;
uniform vec4 lightLambert ;
uniform vec4 lightAmbient ; const mat4 biasMatrix = mat4(0.5 , 0.0 , 0.0 , 0.0 ,
0.0 , 0.5 , 0.0 , 0.0 ,
0.0 , 0.0 , 0.5 , 0.0 ,
0.5 , 0.5 , 0.5 , 1.0 ) ;
void main()
{
worldCoord = gl_ModelViewMatrix * gl_Vertex ;
normal = normalize(gl_NormalMatrix * gl_Normal);
lightVertex = lightProj * lightView * worldCoord ;
lightVertex = lightVertex / lightVertex.w ;
lightVertex = biasMatrix * lightVertex ;
//lightVertex = lightVertex / lightVertex.w ;
gl_TexCoord[] = gl_MultiTexCoord0 ;
color = gl_Color ;
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex ;
}

ShadowMap.frag

uniform sampler2D  tex ;
uniform vec4 lightPos ;
uniform vec4 lightLambert ;
uniform vec4 lightAmbient ;
uniform float m ;
uniform float ka ;
uniform float kd ;
uniform float ks ;
varying vec3 normal ;
varying vec4 lightVertex ;
varying vec4 color ;
varying vec4 worldCoord ;
void main()
{
vec2 poissonDisk[] ;
poissonDisk[] = vec2( -0.94201624, -0.39906216 ) ;
poissonDisk[] = vec2( 0.94558609, -0.76890725 ) ;
poissonDisk[] = vec2( -0.094184101, -0.92938870 );
poissonDisk[] = vec2( 0.34495938, 0.29387760 ) ;
vec3 l = normalize(lightPos - worldCoord).xyz ;
vec3 n = normalize(normal) ;
vec3 v = normalize(-worldCoord).xyz ;
float d = max(dot(n , l) , 0.0) ;
float s = max(dot(normalize(v + l) , n) , 0.0) ;
float cosTheta = dot(n , l) ;
float bias = m*tan(acos(cosTheta));
float shadow = 1.0 ;
//使用poissonDisk采样优化锯齿
for (int i = ; i < ; ++i)
{
if ( texture2D( tex, lightVertex.xy + poissonDisk[i]/700.0 ).z < lightVertex.z-bias ){
shadow -=0.2;
}
}
//不使用优化
/*float depth = texture2D(tex , lightVertex.xy).z ; if (depth + bias < lightVertex.z )
{
shadow = 0.2 ;
}*/
// shadow = 0.5 ;
vec4 finalColor = vec4(0.0 , 0.0 , 0.0 , 1.0) ;
//finalColor *= shadow ;
finalColor = (finalColor + ks * s * lightLambert + ka*lightAmbient + kd * d * lightLambert)*shadow;
gl_FragColor = finalColor ;
}

Shadow Map 实现极其细节的更多相关文章

  1. [工作积累] shadow map问题汇总

    1.基本问题和相关 Common Techniques to Improve Shadow Depth Maps: https://msdn.microsoft.com/en-us/library/w ...

  2. Shadow Map阴影贴图技术之探 【转】

    这两天勉勉强强把一个shadowmap的demo做出来了.参考资料多,苦头可不少.Shadow Map技术是目前与Shadow Volume技术并行的传统阴影渲染技术,而且在游戏领域可谓占很大优势.本 ...

  3. (转)Shadow Map & Shadow Volume

    转自:http://blog.csdn.net/hippig/article/details/7858574 shadow volume 这个术语几乎是随着 DOOM3 的发布而成为FPS 玩家和图形 ...

  4. [ZZ] Shadow Map

    Shadow Map 如何能够高效的产生更接近真实的阴影一直是视频游戏的一个很有挑战的工作,本文介绍目前所为人熟知的两种阴影技术之一的ShadowMap(阴影图)技术.     ShadowMap技术 ...

  5. Unity基础6 Shadow Map 阴影实现

    这篇实现来的有点墨迹,前前后后折腾零碎的时间折腾了半个月才才实现一个基本的shadow map流程,只能说是对原理理解更深刻一些,但离实际应用估计还需要做很多优化.这篇文章大致分析下shadow ma ...

  6. Unity基础(5) Shadow Map 概述

    这篇是自己看shadow map是的一些笔记,内容稍稍凌乱,如有错误请帮忙纠正 1.常见阴影处理方式 Shadow Map : using Z-Buffer Shadow Mapping 的原理与实践 ...

  7. Shadow Map 原理和改进 【转】

    http://blog.csdn.net/ronintao/article/details/51649664 参考 1.Common Techniques to Improve Shadow Dept ...

  8. UI5 Source code map机制的细节介绍

    在我的博客A debugging issue caused by source code mapping里我介绍了在我做SAP C4C开发时遇到的一个曾经困扰我很久的问题,最后结论是这个问题由于Jav ...

  9. GraphicsLab Project之再谈Shadow Map

    作者:i_dovelemon 日期:2019-06-07 主题:Shadow Map(SM), Percentage Closer Filtering(PCF), Variance Shadow Ma ...

随机推荐

  1. UVA11019 Matrix Matcher【hash傻逼题】【AC自动机好题】

    LINK1 LINK2 题目大意 让你在一个大小为\(n*m\)的矩阵中找大小是\(x*y\)的矩阵的出现次数 思路1:Hash hash思路及其傻逼 你把一维情况扩展一下 一维是一个bas,那你二维 ...

  2. 2018-2019-1 20165212 《信息安全系统设计基础》第八周学习总结(pwd)

    2018-2019-1 20165212 <信息安全系统设计基础>第八周学习总结 一.知识点总结 1.三种并发方式 构造并发程序的方法有三种: 进程 线程 I/O多路复用 进程:用内核来调 ...

  3. HDU5033 Building(单调栈)

    题意是说在水平轴上有很多建筑物(没有宽度),知道每个建筑物的位置与高度.有m个查询,每次查询位置x所能看到的天空的角度. 方法是将建筑与查询一起排序,从左往右计算一遍,如果是建筑物,则比较最后两个(当 ...

  4. jquery插件与扩展一

    要说jQuery 最成功的地方,我认为是它的可扩展性吸引了众多开发者为其开发插件,从而建立起了一个生态系统.这好比大公司们争相做平台一样,得平台者得天下.苹果,微软,谷歌等巨头,都有各自的平台及生态圈 ...

  5. graphql-yoga interface && union 使用

      接口就是一个约定,方便数据的约定,union 可以实现数据类型的共享,减少代码量 基本项目 参考 https://github.com/rongfengliang/graphql-yoga-doc ...

  6. Python程序,辅助微信跳一跳游戏介绍

    一.思路介绍 1. 通过Python自动手机截屏,并保存到电脑 2. 通过PhotoShop测量要跳的距离 3. 通过Excel计算按压时间 4. 通过CMD命令控制手机按压时间 二.实现方法详解 1 ...

  7. 洛谷4294 [WC2008]游览计划——斯坦纳树

    题目:https://www.luogu.org/problemnew/show/P4294 大概是状压.两种转移,一个是以同一个点为中心,S由自己的子集拼起来:一个是S相同.中心不同的同层转移. 注 ...

  8. 'scalar deleting destructor' 和 'vector deleting destructor'的区别

    在用到delete的时候,我们往往会针对类对象与类对象数组做不同删除,在这背后编译器是如何做的? #include<iostream> using namespace std; class ...

  9. MHA配置文件说明

    root@192.168.0.20 ~]# cat /etc/masterha/app1.cnf [server default] manager_workdir=/var/log/masterha/ ...

  10. erlang的一些系统限制修改

    atom个数限制 +t xxx 进程数限制 +P xxxx ets表个数限制 +e xxx ports个数限制 +Q xxxx 查看限制 string:tokens(binary_to_list(er ...