OpenGL Advance 深度测试
开启深度测试 glEnable(GL_DEPTH_TEST);
每次清空深度缓存 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
设定深度缓存对比函数 glDepthFunc(GL_LESS);
深度值推导
模板缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 glEnable (GL_STENCIL_TEST);glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);glStencilMask (OxFF);允许模板缓存写入数据glStencilMask (0x00 );禁止模板缓存写入数据glStencilFunc (GL_EQUAL, 1 , 0xFF )参数1 :什么情况下通过模板测试GL_NEVER,GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL, GL_EQUAL, GL_NOTEQUAL, GL_ALWAYS 参数2 :对比的数据对象 参数3 :对比之前,缓存内数据与对比数据都要跟它做一次与操作 glStencilOp (GLenum sfail, GLenum dpfail, GLenum dppass)在模板测试之后,根据不同的测试情况,做出不同反应 sfail:如果本Fragment模板测试没通过,怎么做 dpfail:如果本Fragment模板测试通过,深度测试没通过,怎么做 dppass:如果本Fragment模板测试跟深度测试都通过了,怎么做
高光边缘(选中效果)原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 void rend () { glEnable (GL_DEPTH_TEST); glEnable (GL_STENCIL_TEST); glStencilMask (0xFF ); glClearColor (0.0f , 0.0f , 0.0f , 1.0f ); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glm::vec3 cubePositions[] = { glm::vec3 (0.0f , 0.0f , 0.0f ), glm::vec3 (2.0f , 5.0f , -15.0f ), glm::vec3 (-1.5f , -2.2f , -2.5f ), glm::vec3 (-3.8f , -2.0f , -12.3f ), glm::vec3 (2.4f , -0.4f , -3.5f ), glm::vec3 (-1.7f , 3.0f , -7.5f ), glm::vec3 (1.3f , -2.0f , -2.5f ), glm::vec3 (1.5f , 2.0f , -2.5f ), glm::vec3 (1.5f , 0.2f , -1.5f ), glm::vec3 (-1.3f , 1.0f , -1.5f ) }; glm::vec3 pointLightPositions[] = { glm::vec3 (0.7f , 0.2f , 2.0f ), glm::vec3 (2.3f , -3.3f , -4.0f ), glm::vec3 (-4.0f , 2.0f , -12.0f ), glm::vec3 (0.0f , 0.0f , -3.0f ) }; _camera.update (); _projMatrix = glm::perspective (glm::radians (45.0f ), (float )_width / (float )_height, 0.1f , 100.0f ); glm::mat4 _modelMatrix(1.0f ); _modelMatrix = glm::translate (_modelMatrix, glm::vec3 (0.0f , 0.0f , -3.0f )); glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, _textureBox); glActiveTexture (GL_TEXTURE1); glBindTexture (GL_TEXTURE_2D, _textureSpec); _shader_scene.start (); _shader_scene.setVec3 ("view_pos" , _camera.getPosition ()); _shader_scene.setInt ("myMaterial.m_specular" , 1 ); _shader_scene.setFloat ("myMaterial.m_shiness" , 32 ); _shader_scene.setMatrix ("_viewMatrix" , _camera.getMatrix ()); _shader_scene.setMatrix ("_projMatrix" , _projMatrix); _shader_scene.setVec3 ("_dirLight.m_direction" , glm::vec3 (-0.2f , -1.0f , -0.3f )); _shader_scene.setVec3 ("_dirLight.m_ambient" , glm::vec3 (0.05f , 0.05f , 0.05f )); _shader_scene.setVec3 ("_dirLight.m_diffuse" , glm::vec3 (0.4f , 0.4f , 0.4f )); _shader_scene.setVec3 ("_dirLight.m_specular" , glm::vec3 (0.5f , 0.5f , 0.5f )); _shader_scene.setVec3 ("_pointLight[0].m_pos" , pointLightPositions[0 ]); _shader_scene.setVec3 ("_pointLight[0].m_ambient" , glm::vec3 (0.05f , 0.05f , 0.05f )); _shader_scene.setVec3 ("_pointLight[0].m_diffuse" , glm::vec3 (0.8f , 0.8f , 0.8f )); _shader_scene.setVec3 ("_pointLight[0].m_specular" , glm::vec3 (1.0f , 1.0f , 1.0f )); _shader_scene.setFloat ("_pointLight[0].m_c" , 1.0f ); _shader_scene.setFloat ("_pointLight[0].m_l" , 0.09 ); _shader_scene.setFloat ("_pointLight[0].m_q" , 0.032 ); _shader_scene.setVec3 ("_pointLight[1].m_pos" , pointLightPositions[1 ]); _shader_scene.setVec3 ("_pointLight[1].m_ambient" , glm::vec3 (0.05f , 0.05f , 0.05f )); _shader_scene.setVec3 ("_pointLight[1].m_diffuse" , glm::vec3 (0.8f , 0.8f , 0.8f )); _shader_scene.setVec3 ("_pointLight[1].m_specular" , glm::vec3 (1.0f , 1.0f , 1.0f )); _shader_scene.setFloat ("_pointLight[1].m_c" , 1.0f ); _shader_scene.setFloat ("_pointLight[1].m_l" , 0.09 ); _shader_scene.setFloat ("_pointLight[1].m_q" , 0.032 ); _shader_scene.setVec3 ("_pointLight[2].m_pos" , pointLightPositions[2 ]); _shader_scene.setVec3 ("_pointLight[2].m_ambient" , glm::vec3 (0.05f , 0.05f , 0.05f )); _shader_scene.setVec3 ("_pointLight[2].m_diffuse" , glm::vec3 (0.8f , 0.8f , 0.8f )); _shader_scene.setVec3 ("_pointLight[2].m_specular" , glm::vec3 (1.0f , 1.0f , 1.0f )); _shader_scene.setFloat ("_pointLight[2].m_c" , 1.0f ); _shader_scene.setFloat ("_pointLight[2].m_l" , 0.09 ); _shader_scene.setFloat ("_pointLight[2].m_q" , 0.032 ); _shader_scene.setVec3 ("_pointLight[3].m_pos" , pointLightPositions[3 ]); _shader_scene.setVec3 ("_pointLight[3].m_ambient" , glm::vec3 (0.05f , 0.05f , 0.05f )); _shader_scene.setVec3 ("_pointLight[3].m_diffuse" , glm::vec3 (0.8f , 0.8f , 0.8f )); _shader_scene.setVec3 ("_pointLight[3].m_specular" , glm::vec3 (1.0f , 1.0f , 1.0f )); _shader_scene.setFloat ("_pointLight[3].m_c" , 1.0f ); _shader_scene.setFloat ("_pointLight[3].m_l" , 0.09 ); _shader_scene.setFloat ("_pointLight[3].m_q" , 0.032 ); _shader_scene.setVec3 ("_spotLight.m_pos" , _camera.getPosition ()); _shader_scene.setVec3 ("_spotLight.m_direction" , _camera.getDirection ()); _shader_scene.setVec3 ("_spotLight.m_ambient" , glm::vec3 (0.0f , 0.0f , 0.0f )); _shader_scene.setVec3 ("_spotLight.m_diffuse" , glm::vec3 (1.0f , 1.0f , 1.0f )); _shader_scene.setVec3 ("_spotLight.m_specular" , glm::vec3 (1.0f , 1.0f , 1.0f )); _shader_scene.setFloat ("_spotLight.m_c" , 1.0f ); _shader_scene.setFloat ("_spotLight.m_l" , 0.09 ); _shader_scene.setFloat ("_spotLight.m_q" , 0.032 ); _shader_scene.setFloat ("_spotLight.m_cutOff" , glm::cos (glm::radians (12.5f ))); _shader_scene.setFloat ("_spotLight.m_outCutOff" , glm::cos (glm::radians (15.0f ))); for (int i = 0 ; i < 10 ; i++) { glStencilFunc (GL_ALWAYS, 1 , 0xFF ); glStencilMask (0x00 ); if (i == 4 ) { glStencilFunc (GL_ALWAYS, 1 , 0xFF ); glStencilMask (0xFF ); glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE); } _modelMatrix = glm::mat4 (1.0f ); _modelMatrix = glm::translate (_modelMatrix, cubePositions[i]); _modelMatrix = glm::rotate (_modelMatrix, glm::radians (i * 20.0f ), glm::vec3 (0.0f , 1.0f , 0.0f ));Fre _shader_scene.setMatrix ("_modelMatrix" , _modelMatrix); glBindVertexArray (VAO_cube); glDrawArrays (GL_TRIANGLES, 0 , 36 ); } _shader_scene.end (); _shader_color.start (); _shader_color.setMatrix ("_viewMatrix" , _camera.getMatrix ()); _shader_color.setMatrix ("_projMatrix" , _projMatrix); for (int i = 0 ; i < 10 ; i++) { glStencilFunc (GL_NEVER, 1 , 0xFF ); glStencilMask (0x00 ); if (i == 4 ) { glStencilFunc (GL_NOTEQUAL, 1 , 0xFF ); } _modelMatrix = glm::mat4 (1.0f ); _modelMatrix = glm::translate (_modelMatrix, cubePositions[i]); _modelMatrix = glm::rotate (_modelMatrix, glm::radians (i * 20.0f ), glm::vec3 (0.0f , 1.0f , 0.0f )); _modelMatrix = glm::scale (_modelMatrix, glm::vec3 (1.1f , 1.1f , 1.1f )); _shader_color.setMatrix ("_modelMatrix" , _modelMatrix); glBindVertexArray (VAO_cube); glDrawArrays (GL_TRIANGLES, 0 , 36 ); } _shader_color.end (); glStencilFunc (GL_ALWAYS, 1 , 0xFF ); _shader_sun.start (); _shader_sun.setMatrix ("_viewMatrix" , _camera.getMatrix ()); _shader_sun.setMatrix ("_projMatrix" , _projMatrix); for (int i = 0 ; i < 4 ; i++) { _modelMatrix = glm::mat4 (1.0f ); _modelMatrix = glm::translate (_modelMatrix, pointLightPositions[i]); _modelMatrix = glm::scale (_modelMatrix, glm::vec3 (0.2f , 0.2f , 0.2f )); _shader_sun.setMatrix ("_modelMatrix" , _modelMatrix); glBindVertexArray (VAO_sun); glDrawArrays (GL_TRIANGLES, 0 , 36 ); } _shader_sun.end (); }
Blending 效果 透明的原理 RGBA格式A这个值,即ALPHA
Alpha定义:本色块,在与ColorBuffer当中的颜色产生混合的时候,所占有的比例
颜色混合公式:
Color = source* alpha + destination * (1-alpha)
source:要绘制的物体本Fragment的像素颜色值 alpha:要绘制的物体本Fragment的Alpha值 destination:ColorBuffer里面目标点的颜色值
接口 glEnable(GL_BLEND);
第一步,计算出源颜色与目标颜色 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
第二步,设置计算后的SRC颜色与DST颜色之间的计算方式 glBlendEquation(GLenum mode)
1 2 3 4 5 GL_FUNC_ADD: the default, adds both colors to each other: Cresult = Src + Dst. GL_FUNC_SUBTRACT: subtracts both colors from each other: Cresult = Src - Dst. GL_FUNC_REVERSE_SUBTRACT: subtracts both colors, but reverses order: Cresult = Dst - Src. GL_MIN: takes the component-wise minimum of both colors: Cresult = min(Dst, Src). GL_MAX: takes the component-wise maximum of both colors: Cresult = max(Dst, Src).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 void rend () { std::vector<glm::vec3> _window_pos { glm::vec3 (-1.5f , 0.0f , -0.48f ), glm::vec3 (1.5f , 0.0f , 0.51f ), glm::vec3 (0.0f , 0.0f , 0.7f ), glm::vec3 (-0.3f , 0.0f , -2.3f ), glm::vec3 (0.5f , 0.0f , -0.6f ) }; std::map<float , glm::vec3> _window_sort; for (int i = 0 ; i < _window_pos.size (); i++) { float _dist = glm::length (_camera.getPosition () - _window_pos[i]); _window_sort[_dist] = _window_pos[i]; } glClearColor (0.0f , 0.0f , 0.0f , 1.0f ); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable (GL_DEPTH_TEST); glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); _camera.update (); _projMatrix = glm::perspective (glm::radians (45.0f ), (float )_width / (float )_height, 0.1f , 100.0f ); glm::mat4 _modelMatrix(1.0f ); _modelMatrix = glm::translate (_modelMatrix, glm::vec3 (0.0f , 0.0f , -3.0f )); glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, _textureBox); _shader.start (); _shader.setMatrix ("_modelMatrix" , _modelMatrix); _shader.setMatrix ("_viewMatrix" , _camera.getMatrix ()); _shader.setMatrix ("_projMatrix" , _projMatrix); glBindVertexArray (VAO_plane); glDrawArrays (GL_TRIANGLES, 0 , 6 ); glBindVertexArray (VAO_cube); glDrawArrays (GL_TRIANGLES, 0 , 36 ); for (std::map<float , glm::vec3>::reverse_iterator _it = _window_sort.rbegin (); _it != _window_sort.rend (); _it++) { _modelMatrix = glm::mat4 (1.0f ); _modelMatrix = glm::translate (_modelMatrix, _it->second); _shader.setMatrix ("_modelMatrix" , _modelMatrix); glBindTexture (GL_TEXTURE_2D, _textureWindow); glBindVertexArray (VAO_window); glDrawArrays (GL_TRIANGLES, 0 , 6 ); } _shader.end (); } uint createPlane () { uint _VAO = 0 ; uint _VBO = 0 ; glGenVertexArrays (1 , &_VAO); glBindVertexArray (_VAO); glGenBuffers (1 , &_VBO); glBindBuffer (GL_ARRAY_BUFFER, _VBO); float planeVertices[] = { 5.0f , -0.5f , 5.0f , 2.0f , 0.0f , -5.0f , -0.5f , 5.0f , 0.0f , 0.0f , -5.0f , -0.5f , -5.0f , 0.0f , 2.0f , 5.0f , -0.5f , 5.0f , 2.0f , 0.0f , -5.0f , -0.5f , -5.0f , 0.0f , 2.0f , 5.0f , -0.5f , -5.0f , 2.0f , 2.0f }; glBufferData (GL_ARRAY_BUFFER, sizeof (planeVertices), planeVertices, GL_STATIC_DRAW); glEnableVertexAttribArray (0 ); glVertexAttribPointer (0 , 3 , GL_FLOAT, GL_FALSE, 5 * sizeof (float ), (void *)0 ); glEnableVertexAttribArray (1 ); glVertexAttribPointer (1 , 2 , GL_FLOAT, GL_FALSE, 5 * sizeof (float ), (void *)(3 * sizeof (float ))); return _VAO; }
为了避免深度缓存的问题 我们根据相机的距离先绘制最远的窗体
cullFace表面剔除 解决多余的绘制
定义面的顶点绘制顺序
接口 1 2 3 4 5 6 7 8 glEnable (GL_CULL_FACE);glCullFace (GL_FRONT);GL_BACK: Culls only the back fases. GL_FRONT: Culls only the front faces. GL_FRONT_AND_BACK: Culls both the front and back faces. glFrontFace (GL_CCW); 也可以是GL_CW
glFrontFace 定义正面是顺时针还是逆时针
1 2 3 4 5 6 7 glClearColor (0.0f , 0.0f , 0.0f , 1.0f );glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable (GL_DEPTH_TEST);glEnable (GL_CULL_FACE);glFrontFace (GL_CCW);
FramBuffer 帧缓存 帧缓存不是指一块显存,而是类似于VAO的一个组织结构,他里面包含了很多附件 (ColorBuffer,DepthBuffer,StencilBuffer),附件里面会根据不同需求开辟不同的 显存空间
长期以来,我们都是用默认的帧缓存来做渲染,默认的是0号帧缓存
创建一个属于我们自己的额外的帧缓存,并且利用它来做后处 理(Post-Process),这种渲染方式为离屏渲染(off-screen rending)
创建帧缓存 创建帧缓存ID的方式跟vao vbo texture等一样
1 2 3 4 5 6 7 8 9 unsigned int fbo;glGenFramebuffers (1 , &fbo);glBindFramebuffer (GL_FRAMEBUFFER, fbo);if (glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)glBindFramebuffer (GL_FRAMEBUFFER,0 );glDeleteFramebuffers (1 ,&fbo);
原则:
帧缓存需要至少绑定一个附件(color depth stencil)
至少有一个ColorBuffer绑定
每个绑定的附件必须都开辟内存完毕
每个绑定的附件必须都有相同的采样标准(N*M个像素数据信息)
Texture 作为Buffer附件ColorBuffer 创建Texture:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 unsigned int texture;glGenTextures (1 , &texture);glBindTexture (GL_TEXTURE_2D, texture);glTexlmage2D (GL_TEXTURE_2D, 0 , GL_RGB, 800 , 600 , 0 , GL_RGB, GL_UNSIGNED_BYTE, NULL );glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENTO, GL_TEXTURE_2D, texture, 0 );创建Texture: glTexlmage2D (GL_TEXTURE_2D, 0 , GL_DEPTH24_STENCIL8, 800 , 600 , 0 , GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL ); glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0 );
RenderBuffer作为Buffer附件ColorBuffer 创建RenderBuffer 1 2 3 4 5 6 uint colorBuffer; glGenRenderbuffers (1 , &colorBuffer);glBindRenderbuffer (GL_RENDERBUFFER, colorBuffer);glRenderbufferStorage (GL_RENDERBUFFER, GL_RGBA, 800 , 600 );glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENTO, GL_RENDERBUFFER, colorBuffer);
Texture作为ColorBuffer,RenderBuffer作为D/S Buffer
FrameBuffer 渲染流程
CubeMap
s 坐标 : 表示纹理坐标系中沿水平轴(X 轴)的坐标。
t 坐标 : 表示纹理坐标系中沿垂直轴(Y 轴)的坐标。
r 坐标 : 表示与立方体面法线方向相关的深度坐标(Z 轴)。
CubeMap接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 unsigned int texturelD; glGenTextures (1 , &textureID); glBindTexture (GL_TEXTURE_CUBE_MAP, textureID); int width, height, nrChannels; unsigned char *data; for (unsigned int i = 0 ; i < textures_faces.size (); i++) { data = stbi_load (textures_faces[i].c_str (), &width, &height, &nrChannels, 0 ); glTexlmage2D ( GL_TEXTURE_CUBE_MAP_POSITIVE_X +i, 0 , GL_RGB, width, height, 0 , GL_RGB, GL_UNSIGNED_BYTE, data); } glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); }
天空盒理论
设置一个正方体VAO,每个方向的取值范围是-1到1,并且质心位于坐标原点
做一个CubeMap的Texture并且为六个面都设置目标贴图
在正方体VertexShader当中,把坐标直接传给FragmentShader(插值后的结果),并且去掉摄像机矩阵对于平移的影响
在FragmentShader当中利用坐标来做STR坐标,采样CubeMap给到outFrag
但是每次都必须先绘制天空盒
天空盒Shader解析 VertexShader 1 2 3 4 5 6 7 8 9 10 11 12 13 #version 330 core layout (location = 0 ) in vec3 aPos;out vec3 TexCoords;uniform mat4 projection;uniform mat4 view;void main(){ TexCoords = aPos; gl_Position = projection * view * vec4 (aPos, 1.0 ); }
FragmentShader 1 2 3 4 5 6 7 FragmentShader: in vec3 TexCoords; uniform samplerCube cubemap; void main () { FragColor = texture (cubemap, TexCoords); }
直接用物理坐标给纹理坐标赋值
天空盒优化 问题: 每次都需要优先绘制天空盒,天空盒铺满整个屏幕,所以对于性能还是有一定的损耗,如何能够将天空盒的深度都做到最深呢?
优化: gl_Position变量在每次设置的时候,都是非NDC坐标,他跟NDC之间唯一的差距在于还没 有对xyz除以w值,在绘制天空盒的时候,将gl_Position变量设置为pos.xyww,gl_Position就会在 下一阶段计算深度值z得时候,用当前的Z除以w,即w/W,直接得到Z=1,即最大深度
1 2 3 4 5 6 7 8 9 void main() { TexCoords = aPos; vec4 pos = projection * view * vec4(aPos, 1.0); gl_Position = pos.xyww; } gl_Position-gl_FragCoord,
环境贴图 反射
Shader
Normal = mat3(transpose(inverse(model))) aNormal *
折射
数据接口 定点数据拷贝 1 glBufferSubData (GL_ARRAY_BUFFER, 24 sizeof (data), &data);
向Buffer中进行定点数据拷贝
进行不同的数据打包方式
在不同的buffer之间拷贝数据
拷贝的数据指针
多次修改数据,会造成性能上的浪费, 每次从内存将数据拷贝到显存
glMapBuffer 虚拟指针
顶点数据打包方式
buffer之间的数据拷贝 1 2 3 glBindBuffer (GL_COPY_READ_BUFFER, vbo1)glBindBuffer (GL_COPy_WRITE_BUFFER, vbo2)glCopyBufferSubData (GL_COPY_READ_BUFFER, GL_COPY_WIRTE_BBUFFER, 0 , 0 , 8 *sizeof (float ))
内置变量
接口数据块 Blocks
Uniform变量在UBO显存重点存储方式:Shared,std140,packed,std430
**shared 默认方式 ** std140
UBO 生成过程
先绑定UBO 然后根据UBO绑定Shader
1 2 3 4 5 6 7 glBindBufferBase (GL_UNIFORM_BUFFER, 2 , uboExampleBlock); glBindBufferRange (GL_UNIFORM_BUFFER, 2 , uboExampleBlock, 0 , 152 ); unsigned int lights_index = glGetUniformBlockIndex (shaderA.ID, "Lights" );glUniformBlockBinding (shaderA.ID, lights_index, 2 );
core code 省略后 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 Shader _shader; Shader _shader_red; Shader _shader_green; void initShader () { _shader.initShader ("shader/vertexShader.glsl" , "shader/fragmentShader.glsl" ); _shader_red.initShader ("shader/redShaderv.glsl" , "shader/redShaderf.glsl" ); _shader_green.initShader ("shader/greenShaderv.glsl" , "shader/greenShaderf.glsl" ); } uint createUBO () { uint _ubo = 0 ; glGenBuffers (1 , &_ubo); glBindBuffer (GL_UNIFORM_BUFFER, _ubo); glBufferData (GL_UNIFORM_BUFFER, 2 * sizeof (glm::mat4), NULL , GL_DYNAMIC_DRAW); glBindBuffer (GL_UNIFORM_BUFFER, 0 ); return _ubo; } void bindShaderData () { glBindBufferBase (GL_UNIFORM_BUFFER, 0 , UBO); uint redIndex = glGetUniformBlockIndex (_shader_red.getProgram (), "MAT" ); glUniformBlockBinding (_shader_red.getProgram (), redIndex, 0 ); uint greenIndex = glGetUniformBlockIndex (_shader_green.getProgram (), "MAT" ); glUniformBlockBinding (_shader_green.getProgram (), greenIndex, 0 ); } void rend () { glBufferSubData (GL_UNIFORM_BUFFER, 0 , sizeof (glm::mat4), glm::value_ptr (_projMatrix)); glBufferSubData (GL_UNIFORM_BUFFER, sizeof (glm::mat4), sizeof (glm::mat4), glm::value_ptr (_camera.getMatrix ())); _shader_red.start (); _shader_red.setMatrix ("_modelMatrix" , _modelMatrix); glBindVertexArray (VAO_cube); glDrawArrays (GL_TRIANGLES, 0 , 36 ); _shader_red.end (); _shader_green.start (); _modelMatrix = glm::mat4 (1.0f ); _modelMatrix = glm::translate (_modelMatrix, glm::vec3 (2.0f , 0 , -3.0f )); _shader_green.setMatrix ("_modelMatrix" , _modelMatrix); glBindVertexArray (VAO_cube); glDrawArrays (GL_TRIANGLES, 0 , 36 ); _shader_green.end (); }
GeometryShader 几何着色器 几何着色器原理
几何着色器对装配的顶点进行生成或减除操作 重新定义图元的类型
点输入
三角形输入
每三个一组创建一个gl_in将变量放到gl_in中,输送给Geometry Shading
Geometry 输出 重新定义了图元装配的类型
GeometryShader 1 2 3 4 5 6 7 8 9 10 11 12 13 # version 330 core layout (point)in ;layout (line_strip , max_vertices =2 ) out ;void main(){ gl_Position == gl_in [0 ].gl_Position + vec4 (-0.1 , 0.0 , 0.0 , 0.0 ); EmitVertex (); gl_Position = gl_in [0 ].gl_Position + vec4 (0.1 , 0.0 , 0.0 , 0.0 ); Emitvertex(); EndPrimitive (); }
gl_in 1 2 3 4 5 6 in gl_Vertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; }gl_in[];
法向量可视化
STEP1 正常渲染模型
STEP2 使用geometryShader方式渲染模型,得到法向量
VertexShader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #version 330 core layout (location = 0 ) in vec3 aPos;layout (location = 1 ) in vec3 aNormal;out vec3 normal;uniform mat4 view;uniform mat4 model;void main(){ gl_position = view * model * vec4 (aPos, 1.0 ); mat3 normalMatrix = mat3 (transpose (inverse (view * model))); normal = normailize(vec3 (normalMatrix * aNormal, 0.0 )); }
GeometryShader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 layout (triangles ) in ;layout (line_strip , max_vertices = 6 ) out ;in vec3 normal;const float MAGNITUDE = 0.4 luniform mat4 projection;void GenerateLine(int index ){ gl_Position = projection * gl_in [index ].gl_position; EmitVertex (); gl_Position = projection * (gl_in [index ].gl_position + vec4 (normal, 0.0 ) * MAGNITUDE); EmitVertex (); EndPrimitive (); }
模型读取 模型材质
Assimp库(读取)
Scene
mRootNode 场景根节点
mMeshs[] 模型顶点信息综合
mMaterials[] 模型贴图信息
Child node 部分模型信息
Mesh
模型块,包含构建模型块的所有顶点信息(坐标,法线,UV,索引,纹理等)
模型块数据结构 Vertex Texture Index
函数:构造函数,VBO/VAO 设置函数, 绘制函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 namespace FF{ ffBone::ffBone (int _id, std::string _name, aiMatrix4x4 _aiMatrix) { m_id = _id; m_name = _name; m_offsetMat = AiToGLMMat4 (_aiMatrix); m_parent = NULL ; m_parentSkeleton = NULL ; m_aiAnim = NULL ; m_aiNode = NULL ; } ffBone::ffBone (int _id, std::string _name, glm::mat4 _matrix) { m_id = _id; m_name = _name; m_offsetMat = _matrix; m_parent = NULL ; m_aiAnim = NULL ; m_aiNode = NULL ; } glm::mat4 ffBone::getParentsTransform () { ffBone* _pBone = m_parent; std::vector<glm::mat4> _matrixArr; glm::mat4 _tmpMat = glm::mat4 (1.0f ); while (_pBone) { _tmpMat = AiToGLMMat4 (_pBone->m_aiNode->mTransformation); _matrixArr.push_back (_tmpMat); _pBone = _pBone->m_parent; } glm::mat4 _concatenatedTransform = glm::mat4 (1.0f ); for (uint i = _matrixArr.size () - 1 ; i > -1 ; --i) { _concatenatedTransform *= _matrixArr[i]; } return _concatenatedTransform; } void ffSkeleton::init (std::vector<ffBone> _boneArr, glm::mat4 _globalInverseTransform) { m_globalInverseTransform = _globalInverseTransform; for (uint i = 0 ; i < _boneArr.size (); i++) { m_boneMap[_boneArr[i].m_id] = _boneArr[i]; _boneArr[i].m_parentSkeleton = this ; } } void ffSkeleton::updateBoneMats () { } void ffSkeleton::update () {} ffMesh::ffMesh (std::vector<ffVertex> _vertexVec, std::vector<uint> _indexVec, std::vector<ffTexture> _texVec) { m_vertexVec = _vertexVec; m_indexVec = _indexVec; m_texVec = _texVec; setupMesh (); } void ffMesh::draw (Shader& _shader) { uint _diffuseN = 1 ; uint _specularN = 1 ; for (uint i = 0 ; i < m_texVec.size (); i++) { glActiveTexture (GL_TEXTURE0 + i); std::string _typeName = m_texVec[i].m_type; std::string _numStr; if (_typeName == TEXTURE_DIFFUSE_STR) { _numStr = std::to_string (_diffuseN++); } if (_typeName == TEXTURE_SPECULAR_STR) { _numStr = std::to_string (_specularN++); } _shader.setFloat ("myMaterial." + _typeName + _numStr, i); glBindTexture (GL_TEXTURE_2D, m_texVec[i].m_id); } glActiveTexture (GL_TEXTURE0); glBindVertexArray (m_VAO); glDrawElements (GL_TRIANGLES, m_indexVec.size (), GL_UNSIGNED_INT, 0 ); glBindVertexArray (0 ); } void ffMesh::setupMesh () { uint _VBO = 0 ; uint _EBO = 0 ; glGenVertexArrays (1 , &m_VAO); glBindVertexArray (m_VAO); glGenBuffers (1 , &_VBO); glBindBuffer (GL_ARRAY_BUFFER, _VBO); glBufferData (GL_ARRAY_BUFFER, sizeof (ffVertex) * m_vertexVec.size (), &m_vertexVec[0 ], GL_STATIC_DRAW); glGenBuffers (1 , &_EBO); glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, _EBO); glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (uint) * m_indexVec.size (), &m_indexVec[0 ], GL_STATIC_DRAW); glEnableVertexAttribArray (SHADER_POSITION_ANCHOR); glVertexAttribPointer (SHADER_POSITION_ANCHOR, 3 , GL_FLOAT, GL_FALSE, sizeof (ffVertex), (void *)0 ); glEnableVertexAttribArray (SHADER_NORMAL_ANCHOR); glVertexAttribPointer (SHADER_NORMAL_ANCHOR, 3 , GL_FLOAT, GL_FALSE, sizeof (ffVertex), (void *)offsetof (ffVertex, m_normal)); glEnableVertexAttribArray (SHADER_TEXCOORD_ANCHOR); glVertexAttribPointer (SHADER_TEXCOORD_ANCHOR, 2 , GL_FLOAT, GL_FALSE, sizeof (ffVertex), (void *)offsetof (ffVertex, m_texCoord)); glEnableVertexAttribArray (SHADER_BONE_WEIGHT_ANCHOR); glVertexAttribPointer (SHADER_BONE_WEIGHT_ANCHOR, 4 , GL_FLOAT, GL_FALSE, sizeof (ffVertex), (void *)offsetof (ffVertex, m_weightArr)); glEnableVertexAttribArray (SHADER_BONE_ID_ANCHOR); glVertexAttribIPointer (SHADER_BONE_ID_ANCHOR, 4 , GL_INT, sizeof (ffVertex), (void *)offsetof (ffVertex, m_idArr)); glBindVertexArray (0 ); } void ffModel::loadModel (std::string _path) { Assimp::Importer _importer; const aiScene* _scene = _importer.ReadFile (_path, aiProcess_Triangulate | aiProcess_FlipUVs); if (!_scene || _scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !_scene->mRootNode) { std::cout << "model read fail!" << std::endl; return ; } m_dir = _path.substr (0 , _path.find_last_of ('/' )); processNode (_scene->mRootNode, _scene); } void ffModel::processNode (aiNode* _node, const aiScene* _scene) { for (uint i = 0 ; i < _node->mNumMeshes; i++) { aiMesh* _mesh = _scene->mMeshes[_node->mMeshes[i]]; m_meshVec.push_back (processMesh (_mesh, _scene, _node, m_skeleton)); } for (uint i = 0 ; i < _node->mNumChildren; i++) { processNode (_node->mChildren[i], _scene); } } ffMesh ffModel::processMesh (aiMesh* _mesh, const aiScene* _scene, const aiNode* _node) { std::vector<ffVertex> _vertexVec; std::vector<uint> _indexVec; std::vector<ffTexture> _texVec; for (uint i = 0 ; i < _mesh->mNumVertices; i++) { ffVertex _vertex; glm::vec3 _pos; _pos.x = _mesh->mVertices[i].x; _pos.y = _mesh->mVertices[i].y; _pos.z = _mesh->mVertices[i].z; _vertex.m_pos = _pos; glm::vec3 _normal; _normal.x = _mesh->mNormals[i].x; _normal.y = _mesh->mNormals[i].y; _normal.z = _mesh->mNormals[i].z; _vertex.m_normal = _normal; if (_mesh->mTextureCoords[0 ]) { glm::vec2 _texCoord; _texCoord.x = _mesh->mTextureCoords[0 ][i].x; _texCoord.y = _mesh->mTextureCoords[0 ][i].y; _vertex.m_texCoord = _texCoord; } _vertexVec.push_back (_vertex); } for (uint i = 0 ; i < _mesh->mNumFaces; i++) { aiFace _face = _mesh->mFaces[i]; for (uint j = 0 ; j < _face.mNumIndices; j++) { _indexVec.push_back (_face.mIndices[j]); } } if (_mesh->mMaterialIndex >= 0 ) { aiMaterial* _mat = _scene->mMaterials[_mesh->mMaterialIndex]; std::vector<ffTexture> _diffuseVec = loadMaterialTextures (_mat, aiTextureType_DIFFUSE, TEXTURE_DIFFUSE_STR); _texVec.insert (_texVec.end (), _diffuseVec.begin (), _diffuseVec.end ()); std::vector<ffTexture> _specularVec = loadMaterialTextures (_mat, aiTextureType_SPECULAR, TEXTURE_SPECULAR_STR); _texVec.insert (_texVec.end (), _specularVec.begin (), _specularVec.end ()); } for (uint i = 0 ; i < _mesh->mNumBones; i++) { aiBone* _aiBone = _mesh->mBones[i]; for (uint j = 0 ; j < _aiBone->mNumWeights; j++) { aiVertexWeight _weight = _aiBone->mWeights[i]; for (uint k = 0 ; k < MAX_BONE_WEIGHT_PER_VERTEX; k++) { _vertexVec[_weight.mVertexId].m_weightArr[k] = _weight.mWeight; _vertexVec[_weight.mVertexId].m_idArr[k] = i; } } for (uint i = 0 ; i < _mesh->mNumBones; i++) { aiBone* _pBone = _mesh->mBones[i]; if (!_pBone) { continue ; } std::string _boneName = _pBone->mName.C_Str (); glm::mat4 _boneMatrix = AiToGLMMat4 (_pBone->mOffsetMatrix); ffBone _newBone(i, _boneName, _boneMatrix); } } return ffMesh (_vertexVec, _indexVec, _texVec); } std::vector<ffTexture> ffModel::loadMaterialTextures (aiMaterial* _mat, aiTextureType _type, std::string _typeName) { std::vector<ffTexture> _texVec; for (uint i = 0 ; i < _mat->GetTextureCount (_type); i++) { ffTexture _tex; aiString _path; _mat->GetTexture (_type, i, &_path); _tex.m_id = ffTextureMananger::getInstance ()->createTexture (_path.C_Str (), m_dir); _tex.m_path = _path.C_Str (); _tex.m_type = _typeName; _texVec.push_back (_tex); } return _texVec; } void ffModel::draw (Shader& _shader) { for (uint i = 0 ; i < m_meshVec.size (); i++) { m_meshVec[i].draw (_shader); } } SINGLE_INSTANCE_SET (ffTextureMananger) uint ffTextureMananger::createTexture (std::string _path) { std::map<std::string, uint>::iterator _it = m_texMap.find (_path); if (_it != m_texMap.end ()) { return _it->second; } ffImage* _image = ffImage::readFromFile (_path.c_str ()); uint _texID = 0 ; glGenTextures (1 , &_texID); glBindTexture (GL_TEXTURE_2D, _texID); glTexImage2D (GL_TEXTURE_2D, 0 , GL_RGBA, _image->getWidth (), _image->getHeight (), 0 , GL_RGBA, GL_UNSIGNED_BYTE, _image->getData ()); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); delete _image; m_texMap[_path] = _texID; return _texID; } uint ffTextureMananger::createTexture (std::string _path, std::string _dir) { return createTexture (_dir + '/' + _path); } }
DrawCall 减少性能开销 使用Instance来做 统一将所有渲染任务压入
1 2 void glDrawArraysInstanced (GLenum mode, Glint first, GLsizei count, GLsizei primcount) ;void glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void * indices, FLsizei primcount) ;
gl_InstanceID 内置变量 代表正在绘制的实例编号 从0开始依次递增
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #version 330 core layout (location = 0 ) in vec2 aPos;layout (location = 1 ) in vec3 aColor;out vec3 fColor;uniform vec2 offsets[100 ];void main(){ vec2 offset = offset [gl_InstanceID ]; gl_Position = vec4 (aPos + offset , 0.0 , 1.0 ); fColor = aColor; }
使用gl_InstanceID 会导致过多的使用uniform变量
gl_InstanceID 内置变量 优化 InstanceArray 利用顶点数组VBO进行书传书 VShader中使用layout进行MAT4的获取
1 2 3 4 5 6 7 8 9 10 11 12 13 glGenBuffers(1 , &_VBO_ins); glBindBuffer(GL_ARRAY_BUFFER, VBO_ins); glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4 ) * AMOUNT, _mMatrixArrp[0 ], GL_STATIC_DRAW); glBIndVertexArray(_model->m_meshVec[i].m_VAO); glEnableVertexAttribArray(3 ); glVertexAttribPointer(3 ,4 ,GL_FLOAT,GL_FALSE,sizeof(glm::mat4 ),(void *)0 ); glEnableVertexAttribArray(4 ); glVertexAttribPointer(4 ,4 ,GL_FLOAT,GL_FALSE,sizeof(glm::mat4 ),(void *)0 (sizeof(glm::vec4 )); glEnableVertexAttribArray(5 ); glVertexAttribPointer(5 ,4 ,GL_FLOAT,GL_FALSE,sizeof(glm::mat4 ),(void *)0 (sizeof(glm::vec4 )*2 ); glEnableVertexAttribArray(6 ); glVertexAttribPointer(6 ,4 ,GL_FLOAT,GL_FALSE,sizeof(glm::mat4 ),(void *)0 (sizeof(glm::vec4 )*3 );
glVertexAttribDivisor(GLuint index, GLuint divisor) index:挂载的锚点
divisor:多少个实例迭代一次, 0 代表一个顶点迭代一次, 1代表一个模型实例绘制迭代一次
Instance 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 #define AMOUNT 100000 glm::mat4* _mMatrixArr = NULL ; FF::ffModel* _model = NULL ; uint _VBO_ins; uint _texId; Shader _shader; Camera _camera; glm::mat4 _projMatrix(1.0f ); int _width = 800 ;int _height = 600 ;void rend () { glClearColor (0.0f , 0.0f , 0.0f , 1.0f ); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable (GL_DEPTH_TEST); _camera.update (); _projMatrix = glm::perspective (glm::radians (45.0f ), (float )_width / (float )_height, 0.1f , 100.0f ); _shader.start (); _shader.setMatrix ("_viewMatrix" , _camera.getMatrix ()); _shader.setMatrix ("_projMatrix" , _projMatrix); _shader.setFloat ("myMaterial.m_shiness" , 32.0f ); _shader.setVec3 ("view_pos" , _camera.getPosition ()); _shader.setVec3 ("myLight.m_pos" , glm::vec3 (0.0f , 0.0f , 0.0f )); _shader.setVec3 ("myLight.m_ambient" , glm::vec3 (0.9f , 0.9f , 0.9f )); _shader.setVec3 ("myLight.m_diffuse" , glm::vec3 (1.0f , 1.0f , 1.0f )); _shader.setVec3 ("myLight.m_specular" , glm::vec3 (1.0f , 1.0f , 1.0f )); _shader.setFloat ("myLight.c" , 1.0f ); _shader.setFloat ("myLight.l" , 0.09f ); _shader.setFloat ("myLight.q" , 0.032f ); glBindTexture (GL_TEXTURE_2D, _texId); for (uint i = 0 ; i < _model->m_meshVec.size (); i++) { glBindVertexArray (_model->m_meshVec[i].m_VAO); glDrawElementsInstanced (GL_TRIANGLES, _model->m_meshVec[i].m_vertexVec.size (), GL_UNSIGNED_INT, 0 , AMOUNT); glBindVertexArray (0 ); } _shader.end (); } void initShader () { _shader.initShader ("shader/vPointShader.glsl" , "shader/fPointShader.glsl" ); } void framebuffer_size_callback (GLFWwindow* window, int width, int height) { glViewport (0 , 0 , width, height); } void processInput (GLFWwindow* window) { if (glfwGetKey (window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose (window, true ); if (glfwGetKey (window, GLFW_KEY_W) == GLFW_PRESS) { _camera.move (CAMERA_MOVE::MOVE_FRONT); } if (glfwGetKey (window, GLFW_KEY_S) == GLFW_PRESS) { _camera.move (CAMERA_MOVE::MOVE_BACK); } if (glfwGetKey (window, GLFW_KEY_A) == GLFW_PRESS) { _camera.move (CAMERA_MOVE::MOVE_LEFT); } if (glfwGetKey (window, GLFW_KEY_D) == GLFW_PRESS) { _camera.move (CAMERA_MOVE::MOVE_RIGHT); } } void mouse_callback (GLFWwindow* window, double xpos, double ypos) { _camera.onMouseMove (xpos, ypos); } void initInstanceArray () { _mMatrixArr = new glm::mat4[AMOUNT]; srand (glfwGetTime ()); float radius = 10.0 ; float offset = 2.5f ; glm::mat4 model = glm::mat4 (1.0f ); for (unsigned int i = 0 ; i < AMOUNT; i++) { glm::mat4 model = glm::mat4 (1.0f ); float angle = (float )i / (float )AMOUNT * 360.0f ; float displacement = (rand () % (int )(2 * offset * 100 )) / 100.0f - offset; float x = sin (angle) * radius + displacement; displacement = (rand () % (int )(2 * offset * 100 )) / 100.0f - offset; float y = displacement * 0.4f ; displacement = (rand () % (int )(2 * offset * 100 )) / 100.0f - offset; float z = cos (angle) * radius + displacement; model = glm::translate (model, glm::vec3 (x, y, z)); float scale = (rand () % 20 ) / 100.0f + 0.05 ; model = glm::scale (model, glm::vec3 (scale)); float rotAngle = (rand () % 360 ); model = glm::rotate (model, glm::radians (rotAngle), glm::vec3 (0.4f , 0.6f , 0.8f )); _mMatrixArr[i] = model; } glGenBuffers (1 , &_VBO_ins); glBindBuffer (GL_ARRAY_BUFFER, _VBO_ins); glBufferData (GL_ARRAY_BUFFER, sizeof (glm::mat4) * AMOUNT, &_mMatrixArr[0 ], GL_STATIC_DRAW); for (uint i = 0 ; i < _model->m_meshVec.size (); i++) { glBindVertexArray (_model->m_meshVec[i].m_VAO); glEnableVertexAttribArray (3 ); glVertexAttribPointer (3 , 4 , GL_FLOAT, GL_FALSE, sizeof (glm::mat4), (void *)0 ); glEnableVertexAttribArray (4 ); glVertexAttribPointer (4 , 4 , GL_FLOAT, GL_FALSE, sizeof (glm::mat4), (void *)(sizeof (glm::vec4))); glEnableVertexAttribArray (5 ); glVertexAttribPointer (5 , 4 , GL_FLOAT, GL_FALSE, sizeof (glm::mat4), (void *)(sizeof (glm::vec4) * 2 )); glEnableVertexAttribArray (6 ); glVertexAttribPointer (6 , 4 , GL_FLOAT, GL_FALSE, sizeof (glm::mat4), (void *)(sizeof (glm::vec4) * 3 )); glVertexAttribDivisor (3 , 1 ); glVertexAttribDivisor (4 , 1 ); glVertexAttribDivisor (5 , 1 ); glVertexAttribDivisor (6 , 1 ); glBindVertexArray (0 ); } _texId = FF::ffTextureManager::getInstance ()->createTexture ("res/rock/rock.png" ); } int main () { glfwInit (); glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3 ); glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 3 ); glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); GLFWwindow* window = glfwCreateWindow (800 , 600 , "OpenGL Core" , NULL , NULL ); if (window == NULL ) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate (); return -1 ; } glfwMakeContextCurrent (window); if (!gladLoadGLLoader ((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1 ; } glViewport (0 , 0 , _width, _height); glfwSetFramebufferSizeCallback (window, framebuffer_size_callback); glfwSetInputMode (window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); glfwSetCursorPosCallback (window, mouse_callback); _camera.lookAt (glm::vec3 (0.0f , 0.0f , 8.0f ), glm::vec3 (0.0f , 0.0f , -1.0f ), glm::vec3 (0.0f , 1.0f , 0.0f )); _camera.setSpeed (0.01f ); _model = new FF::ffModel ("res/rock/rock.obj" ); initInstanceArray (); initShader (); while (!glfwWindowShouldClose (window)) { processInput (window); rend (); glfwSwapBuffers (window); glfwPollEvents (); } glfwTerminate (); return 0 ; }
Blin-Phong 光照实现 半程向量
反射光和人眼的夹角大于九十度 导致光线被计算忽略 造成偏差
实现效果
Gamma矫正 显示器亮度与电压的关系 CRT显示器是通过电压对屏幕亮度进行调节,但是电压的增长与亮度的增长并不是线性相关 的关系,大概会是一个2.2次幂的关系
现代的显示器为了兼容CRT也做了一些类似的设置保留
所以如果想显示真实场景就需要校正
SRGB颜色空间 经过gamma校正后的颜色所构成的颜色空间,即为sRGB颜色空间
颜色:(0.5,0.0,0.0)1/2.2=(0.5,0.0,0.0)0.45=(0.73,0.0,0.0),
那么这个(0.73,0.0,0.0) 即为sRGB颜色空间当中的颜色值了
1 2 3 4 5 6 7 8 9 FUN1: glEnable (GL_FRAMEBUFFER_SRGB);FUN2: float gamma = 2.2 ;FragColor.rgb = pow (fragColor.rgb, vec3 (1.0 /gamma));
再美术工作中 如果工具软件具有Gamma矫正 会输出SRGB颜色空间的贴图 程序解读后进行Gamma矫正会导致效果和美术生成所看到的效果不同
创作者:rgb — 做一次1/2.2次幂(Gamma) —- 美术操作 — 2.2次幂之后 — 正常 程序:读入rgb — 做一次2.2次幂 — 做一次1/2.2次幂 — 做一次2.2次幂 — 正常
1 2 3 float gamma = 2.2 ;vec3 diffuseColor = pow (texture (diffuse, texCoords).rgb, vec3 (gamma));
glTexlmage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
可以如此接口使用,在图片读进来的时候,就设置internalFormat为sRGB空间,然后会被openGL 自动转化为2.2次幂,之后就可以使用, 场景变得柔和
ShadowMapping
平行光 深度渲染 FrameBuffer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 unsigned int depthMapFBO;glGenFramebuffers (1 , &depthMapFBO);const unsigned int SHADOW_WIDTH = 1024 , SHADOW_HEIGHT = 1024 ;unsigned int depthMap;glGenTextures (1 , &depthMap);glBindTexture (GL_TEXTURE_2D, depthMap);glTexlmage2D (GL_TEXTURE_2D, 0 , GL_DEPTH_COMPONENT,SHADOW_WIDTH, SHADOW_HEIGHT, 0 , GL_DEPTH_COMPONENT, GL_FLOAT, NULL ); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glBindFramebuffer (GL_FRAMEBUFFER, depthMapFBO);glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0 );glDrawBuffer (GL_NONE);glReadBuffer (GL_NONE);glBindFramebuffer (GL_FRAMEBUFFER, 0 );
FragmentShader 阴影部分(Pass2) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #version 330 core out vec4 FragColor;[ -- datas -- ] uniform sampler2D shadowMap; float ShadowCalculation(vec4 fragPosLightSpace){ vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5 ; float closestDepth = texture (shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; float shadow = currentDepth > closestDepth ? 1.0 : 0.0 ; return shadow; } void main(){ [ -- calculate Light --- ] float shadow = ShadowCalculation(fs_in.FragPosLightSpace); vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color; FragColor = vec4 (lighting, 1.0 ); }
实际效果
ShadowAnce 噪声 (采样不均问题)
深度差别太小 导致a通过测试 b未通过测试 a亮 b暗 明暗交替
Bias参数去噪
同样但也会导致之前该是阴影的部分抬升
Peter Panning 去噪
过采样问题
XY超出立方体的点处理方式 :使用GL_CLAMP_TO_BORDER的过采样方式,让出界的都采样到1.0的深度
1 2 3 4 glTexParameteri(GL_TEXTURE_2D, GL_TExTURE_WARP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TExTURE_WARP_T, GL_CLAMP_TO_BORDER); float borderColor[] = {1 ,0 f. 1.0 f, 1.0 f, 1.0 f};glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
阴影锯齿问题 深度贴图分辨率有限 采样密度不够
PCF-percentage-closer filtering 最简单的方式 多顶当前要处理的Fragment对应的深度贴图 Texel,使用周围八个Texel进行平均
1 2 3 4 5 6 7 8 9 10 11 12 float shadow = 0.0 ;vec2 texelSize = 1.0 / textureSize (shadowMap, 0 );for (int x =- 1 ; x <= 1 ; ++x){ for (int y =- 1 ; y <= 1 ; ++y) { float pcfDepth = texture (shadowMap, projCoords.xy + vec2 (x, y) * texelSize).r; shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0 ; } } shadow /= 9.0 ;
PointShadow 单一光源方向并不足够 房间中的光照需要优化
每个面对应一个90度的视场角(FOV),覆盖场景的一个特定方向:前(+X),后(-X),左(-Y),右(+Y),上(+Z),下(-Z)。每个面都可以使用一个标准的透视投影矩阵进行渲染,FOV通常设置为90度,以确保没有任何失真或间隙。
首先可以想到为光源的6个方向做6个光照矩阵 6次渲染得到6张深度贴图,进行pass2
1 2 3 4 5 6 for (unsigned int i = 0 ; i < 6 ; i++){ glFramebufferTexture (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, textures[i], 0 ); BindViewMatrix (lightViewMatrices[i]); RenderScene (); }
优化思路
问题描述:我们需要渲染六张图,为什么必须要做六次渲染流程呢?主要是每次都需要虚拟一个光照矩阵(ViewMatrix*ProjMatrix),然后这六个光照矩阵分别跟Vertex相乘做变换,分别渲染
设想:对每个三角形来讲,有没有可能通过一次流程,传入六个光照矩阵,对每个三角形进行六次变换,输出六个三角形
解决:可以通过GeometryShader可以将一个三角形输入做成6个三角形图源输出给FS
问题描述:我们在FS里拿到了六个三角形的数据(分别对应六个光照矩阵以及六个阴影深度贴图),那么我们如何指定哪个三角形渲染到哪个贴图呢?
结论:在GS里,有一个内置变量,叫做gl_Layer,它可以在GS里配合每个输出的图元(三角形), 指定输送给编号为几的Texture,但是只能对CubeMap这种类型管用
问题描述:我现在手里有六个面的六张深度贴图,当我进行真正渲染的时候,需要知道哪个Fragment到底去哪个贴图里读取深度,一般是跟光线连线,然后看看指向哪个面。BUT!太复杂了
结论:对于六个面取纹理这个问题,CubeMap就很好地解决了呀!将光源与当前需要渲染的Fragment位置连一条向量,用这个向量直接作为STR坐标对CubeMap进行采样即可
阴影渲染流程
CubeMap坐标原理 在二维图像纹理中 如果直接读入一张图显示会出现上下翻转。图片的(0,0)在左上角,
OpenGL的UV(0,0)在左下角 读入图片生成纹理之前需要在y反向做翻转
在CubeMap中并没有上下翻转
有一个向量R(xyz),如果使用这个R向当前的CubeMap进行采样,那么是如何被翻译到某一个 Texture的uv上面呢?
1首先我们看下在R的每个分量当中,绝对值最大的是哪个,比如是x绝对值最大,并且x还 是正数,那就说明要冲POSITIVE_x这个Texture进行取值
2计算uv坐标,此时由于冲着x正方向,那么u=z,v=y,一横一竖,但是这里就有问题了!
法线贴图 & TBN空间推导 如何做出物体表面凹凸不平的效果
真实的物体表面应该是粗糙的,几乎每一个Fragment之间的法线都会有所不同
把一张法线贴图做成一个以(0,0)为UV点为原点的笛卡尔坐标系,指向外部的向量N为每个Fragment各自的法向量 按照右手坐标系法则求剩下的TB值 如果N看作坐标系Z轴 B为Y轴T为X轴 每一个法线RGB都可以是坐标系中的向量。
TBN 空间转换 我们目前做出来了一组切线空间的正交基,目前存在两个选择:
1在每一个FragmentShader的执行周期,将每一个法线贴图的normal转换到世界坐标系
2将每一个相关变量都转换到TBN坐标空间,然后与法线贴图的normal进行计算
1 2 3 4 5 6 7 8 9 10 11 12 13 void main(){ vec3 T = normalize (vec3 (model * vec4 (aTangent, 0.0 ))); vec3 B = normalize (vec3 (model * vec4 (aBitangent, 0.0 ))); vec3 N = normalize (vec3 (model * vec4 (aNormal, 0.0 ))); mat3 TBN = mat3 (T, B, N); } normal = texture (normalMap, fs_in.TexCoords).rgb; normal = normal * 2.0 - 1.0 ; normal = normalize (fs_in.TBN * normal);
HDR颜色空间 OpenGL 系统会把超过1的颜色切位1 如果Frag颜色大于1的过多 就会过度曝光
如果场景当中有很多灯光,且彼此差距很大,那么很容易造成某 些Fragment亮度过高。我们有两个处理方式:
调整场景当中光源的强度,使得大部分的Fragment颜色值不会超过1,但是会让场景无法表达本来的信息
允许场景当中存在超过1的颜色值,但是在最后输出颜色的时候, 做统一的转换计算,转换到o-1,从而能够更大程度的保持场景当中的颜色信息
HDR(High Dynamic range)颜色空间方式,允许颜色值大于1的存在
HDR最早用于摄影的曝光拍摄,通过更多的接受光照,从而能够揭示出来光照较少部分的细节
HDR-FloatingPoint Buffers 此时我们假设渲染到某个Framebuffer上面
1 2 glBindTexture (GL_TEXTURE_2D, colorBuffer);glTexlmage2D (GL_TEXTURE_2D, 0 , GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0 , GL_RGBA, GL_FLOAT, NULL );
这里对于一个像素的RGBA值,每个通道使用一个GL_Float存储,并且每个分量占有16bits即2字节
当然如果我们精度要求很高,可以使用GL_RGBA32F,即一个通道32位,4字节。
HDR toneMapping 将颜色从HDR颜色空间转到RGB0-1颜色空间的过程称为toneMapping
如果简单的把HDR空间的颜色线性的映射到RGB空间,根本不会有任何的意义,反而光线暗的地方更加看不到了
我们必须要用其他的变换公式来进行二者之间的映射
Reinhard tone mapping
1 2 3 4 5 6 7 8 9 10 uniform sampler2D hdrBuffer;void main(){ vec3 hdrColor = texture (hdrBuffer, TexCoords).rgb; vec3 mapped = hdrColor / (hdrColor + vec3 (1.0 )); FragColor = vec4 (mapped, 1.0 ); }
Exposure tone mapping
1 2 3 4 5 6 7 8 9 10 11 12 uniform float exposure;void main(){ vec3 hdrColor = texture (hdrBuffer, TexCoords).rgb; vec3 mapped = vec3 (1.0 ) - exp (-hdrColor * exposure); FragColor = vec4 (mapped, 1.0 ); }
Bloom 效果
Bloom 基础做法
将场景渲染出来到FBO的colorBufferA
将灯光单独抠图出来渲染到FBO的colorBufferB
将灯光Texture单独做模糊
将Bloom灯光图合并到主场景Texture中
光源边界问题界定 我们如何界定Texture当中哪些像素是代表着光源呢?如果我们认定大于某个值为光源,那么万一遇到白色物体如何决定?
解决:在渲染的时候使用HDR颜色空间,就可以将光源设置成为大于1的颜色值, 使得光源颜色与其它物体颜色差距变大
1 2 3 4 lightColors.push_back(glm :: vec3 (5.0 f, 5.0 f, 5.0 f)); lightColors.push_back(glm :: vec3 (10.0 f, 0.0 f, 0.0 f)); lightColors.push_back(glm :: vec3 (0.0 f, 0.0 f, 15.0 f)); lightColors.push_back(glm :: vec3 (0.0 f, 5.0 f, 0.0 f));
MRT技术 如何一个PASS完成两个任务:将场景渲染到colorBufferA,将灯光渲染到colorBufferB,并且希望使用同一个FBO
解决:使用MRT技术,即(Multiple Render Target)。在FBO当中,存在多个ColorAttachment选项,我们 可以启动两个Color附件,在FS当中对这两个目标进行渲染。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 unsigned int hdrFBO;glGenFramebufrers (1 , &hdrFBO);glBindFramebuffer (GL_FRAMEBUFFER, hdrFBO);unsigned int colorBuffers[2 ];glGenTextures (2 , colorBuffers);for (unsigned int i = 0 ; i < 2 ; i++)glBindTexture (GL_TEXTURE_2D, colorBuffers[i]);glTexlmage2D (GL_TEXTURE_2D, 0 , GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0 , GL_RGBA, GL_FLOAT, NULL glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glFramebufferTexture2D ( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENTO + i, GL_TEXTURE_2D, colorBuffers[i], 0 );
将两个绘制ColorBuffer目标, 绑定到FS的两个锚定位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 unsigned int attachments[2 ] = { GL_COLOR_ATTACHMENTO, GL_COLOR_ATTACHMENT1 }; glDrawBuffers(2 , attachments); #version 330 core layout (location = 0 ) out vec4 FragColor; layout (location = 1 ) out vec4 BrightColor; uniform vec3 lighting; void main() FragColor = XXXX float brightness = dot (FragColor.rgb, vec3 (0.2126 , 0.7152 , 0.0722 )); if (brightness > 1.0 ) BrightColor = vec4 (FragColor.rgb, 1.0 ); else BrightColor = vec4 (0.0 , 0.0 , 0.0 , 1.0 ); }
实现效果
高斯模糊 拆分降低复杂度
高斯 Shader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 uniform bool horizontal;uniform float weight[5 ] = float [] (0.227027 , 0.1945946 , 0.1216216 , 0.054054 , 0.016216 );void main(){ vec2 tex_offset = 1.0 / textureSize (image, 0 ); vec3 result = texture (image, TexCoords).rgb * weight[0 ]; if (horizontal) { for (int i = 1 ; i < 5 ; ++i) { result += texture (image, TexCoords + vec2 (tex_offset.x * i, 0.0 )).rgb * weight[i]; result += texture (image, TexCoords - vec2 (tex_offset.x * i, 0.0 )).rgb * weight[i]; } } else { for (int i = 1 ; i < 5 ; ++i){ result += texture (image, TexCoords + vec2 (0.0 , tex_offset.y * i)).rgb * weight[i]; result += texture (image, TexCoords - vec2 (0.0 , tex_offset.y * i)).rgb * weight[i]; } } FragColor = vec4 (result, 1.0 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bool horizontal = true , first_iteration = true ;int amount = 10 ;shaderBlur.use (); for (unsigned int i = 0 ; i < amount; i++){ glBindFramebuffer (GL_FRAMEBUFFER, pingpongFBO[horizontal]); shaderBlur.setInt ("horizontal" , horizontal); glBindTexture ( GL_TEXTURE_2D, first_iteration ? colorBuffers[1 ] : pingpongBuffers[!horizontal] ); RenderQuad (); horizontal = !horizontal; if (first_iteration) first_iteration = false ; } glBindFramebuffer (GL_FRAMEBUFFER, 0 );
延迟渲染技术 G缓冲 G缓冲(G-buffer)是对所有用来储存光照相关的数据,并在最后的光照处理阶段中使用的所有纹理的总称。
正向渲染中照亮一个片段所需要的所有数据
一个3D位置 向量来计算(插值)片段位置变量供lightDir
和viewDir
使用
一个RGB漫反射颜色 向量,也就是反照率(Albedo)
一个3D法 向量来判断平面的斜率
一个镜面强度(Specular Intensity)浮点值
所有光源的位置和颜色向量
玩家或者观察者的位置向量
正向渲染(Forward Rendering)或者 正向着色法(Forward Shading),它是我们渲染物体的一种非常直接的方式,在场景中我们根据所有光源照亮一个物体,之后再渲染下一个物体,以此类推。它非常容易理解,也很容易实现,但是同时它对程序性能的影响也很大,因为对于每一个需要渲染的物体,程序都要对每一个光源每一个需要渲染的片段进行迭代,这是 非常 多的!因为大部分片段着色器的输出都会被之后的输出覆盖,正向渲染还会在场景中因为高深的复杂度(多个物体重合在一个像素上)浪费大量的片段着色器运行时间。
**延迟着色法(Deferred Shading), 或者说是延迟渲染(Deferred Rendering)**,为了解决上述问题而诞生了,它大幅度地改变了我们渲染物体的方式。这给我们优化拥有大量光源的场景提供了很多的选择,因为它能够在渲染上百甚至上千光源的同时还能够保持能让人接受的帧率。
**延迟(Defer)或 推迟(Postpone)**大部分计算量非常大的渲染(像是光照)到后期进行处理的想法。它包含两个处理阶段(Pass):在第一个几何处理阶段(Geometry Pass)中,我们先渲染场景一次,之后获取对象的各种几何信息,并储存在一系列叫做G缓冲(G-buffer)的纹理中;想想位置向量(Position Vector)、颜色向量(Color Vector)、法向量(Normal Vector)和/或镜面值(Specular Value)。场景中这些储存在G缓冲中的几何信息将会在之后用来做(更复杂的)光照计算。
保证在G缓冲中的片段和在屏幕上呈现的像素所包含的片段信息是一样的,因为深度测试已经最终将这里的片段信息作为最顶层的片段。这样保证了对于在光照处理阶段中处理的每一个像素都只处理一次,所以我们能够省下很多无用的渲染调用。除此之外,延迟渲染还允许我们做更多的优化,从而渲染更多的光源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 while (...) { glBindFramebuffer (GL_FRAMEBUFFER, gBuffer); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); gBufferShader.Use (); for (Object obj : Objects) { ConfigureShaderTransformsAndUniforms (); obj.Draw (); } glBindFramebuffer (GL_FRAMEBUFFER, 0 ); glClear (GL_COLOR_BUFFER_BIT); lightingPassShader.Use (); BindAllGBufferTextures (); SetLightingUniforms (); RenderQuad (); }
对于几何渲染处理阶段,我们首先需要初始化一个帧缓冲对象,我们很直观的称它为gBuffer
,它包含了多个颜色缓冲和一个单独的深度渲染缓冲对象(Depth Renderbuffer Object)。对于位置和法向量的纹理,我们希望使用高精度的纹理(每分量16或32位的浮点数),而对于反照率和镜面值,使用默认的纹理(每分量8位浮点数)就够了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 GLuint gBuffer; glGenFramebuffers (1 , &gBuffer);glBindFramebuffer (GL_FRAMEBUFFER, gBuffer);GLuint gPosition, gNormal, gColorSpec; glGenTextures (1 , &gPosition);glBindTexture (GL_TEXTURE_2D, gPosition);glTexImage2D (GL_TEXTURE_2D, 0 , GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0 , GL_RGB, GL_FLOAT, NULL );glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0 glGenTextures (1 , &gNormal);glBindTexture (GL_TEXTURE_2D, gNormal);glTexImage2D (GL_TEXTURE_2D, 0 , GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0 , GL_RGB, GL_FLOAT, NULL );glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal, 0 );glGenTextures (1 , &gAlbedoSpec);glBindTexture (GL_TEXTURE_2D, gAlbedoSpec);glTexImage2D (GL_TEXTURE_2D, 0 , GL_RGBA, SCR_WIDTH, SCR_HEIGHT, 0 , GL_RGBA, GL_FLOAT, NULL );glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gAlbedoSpec, 0 );GLuint attachments[3 ] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; glDrawBuffers (3 , attachments);
延迟光照处理 现在我们已经有了一大堆的片段数据储存在G缓冲中供我们处置,我们可以选择通过一个像素一个像素地遍历各个G缓冲纹理,并将储存在它们里面的内容作为光照算法的输入,来完全计算场景最终的光照颜色。由于所有的G缓冲纹理都代表的是最终变换的片段值,我们只需要对每一个像素执行一次昂贵的光照运算就行了。这使得延迟光照非常高效,特别是在需要调用大量重型片段着色器的复杂场景中。
1 2 3 4 5 6 7 8 9 10 11 12 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);shaderLightingPass.Use (); glActiveTexture (GL_TEXTURE0);glBindTexture (GL_TEXTURE_2D, gPosition);glActiveTexture (GL_TEXTURE1);glBindTexture (GL_TEXTURE_2D, gNormal);glActiveTexture (GL_TEXTURE2);glBindTexture (GL_TEXTURE_2D, gAlbedoSpec);SendAllLightUniformsToShader (shaderLightingPass);glUniform3fv (glGetUniformLocation (shaderLightingPass.Program, "viewPos" ), 1 , &camera.Position[0 ]);RenderQuad ();
在渲染之前绑定了G缓冲中所有相关的纹理,并且发送光照相关的uniform变量到着色器中
Shader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #version 330 core out vec4 FragColor;in vec2 TexCoords;uniform sampler2D gPosition;uniform sampler2D gNormal;uniform sampler2D gAlbedoSpec;struct Light { vec3 Position; vec3 Color; }; const int NR_LIGHTS = 32 ;uniform Light lights[NR_LIGHTS];uniform vec3 viewPos;void main(){ vec3 FragPos = texture (gPosition, TexCoords).rgb; vec3 Normal = texture (gNormal, TexCoords).rgb; vec3 Albedo = texture (gAlbedoSpec, TexCoords).rgb; float Specular = texture (gAlbedoSpec, TexCoords).a; vec3 lighting = Albedo * 0.1 ; vec3 viewDir = normalize (viewPos - FragPos); for (int i = 0 ; i < NR_LIGHTS; ++i) { vec3 lightDir = normalize (lights[i].Position - FragPos); vec3 diffuse = max (dot (Normal, lightDir), 0.0 ) * Albedo * lights[i].Color; lighting += diffuse; } FragColor = vec4 (lighting, 1.0 ); }
结合延迟渲染与正向渲染
在延迟渲染方形之上正向渲染所有的光源 正常情况下渲染立方体,只是会在我们完成延迟渲染操作之后进行
首先复制出在几何渲染阶段中储存的深度信息,并输出到默认的帧缓冲的深度缓冲,然后我们才渲染光立方体。这样之后只有当它在之前渲染过的几何体上方的时候,光立方体的片段才会被渲染出来。我们可以使用glBlitFramebuffer
复制一个帧缓冲的内容到另一个帧缓冲中,用来还原多重采样的帧缓冲。glBlitFramebuffer
这个函数允许我们复制一个用户定义的帧缓冲区域到另一个用户定义的帧缓冲区域。
光的体积渲染 问题:如果场景当中存在多个光源,那么即便是使用延迟渲染,那么也会出现每个Fragment跟每个光产生一次渲染的性能问题!
我们能不能对每个光源的辐射范围做一个判断,如果当前的Fragment不在范围内, 就不去计算
Threshold 边界 问题:对于每个光源,如果距离计算衰减到光照为0就表示不用去计算的话,就不会找到边界了。
想法:我们可以设置一个阈值,用如下公式计算距离d,如果大于d的就忽略掉
延迟渲染它本身并不能支持非常大量的光源,因为我们仍然必须要对场景中每一个光源计算每一个片段的光照分量。真正让大量光源成为可能的是我们能够对延迟渲染管线引用的一个非常棒的优化:光体积(Light Volumes)
得出X即为D的阈值
程序流程
GPU再运行if的时候 并行执行 会造成性能丢失
优化
将所有的几何信息写在G-Buffer上面,也就是说我们可以为每一个光照做一个球,然后使用G-Buffer的信息作为绑定纹理, 只渲染每一个光照球,这个球体Fragment范围内的G-Buffer才会被渲染
光源重合问题 使用Blend功能,设置混合模式,比如我们可以设置对物体影响最强的光源为主
1 2 glBlendFuncSeparate (GL_ONE, GL ONE, GL_ONE, GL_ONE);glBlendEquation (GL_MAX);
也可以采用相加方式
CullFace问题 球体存在正反两面 所以会被绘制两次 增大了负担 CullFace会导致进入球体内部不存在渲染
开启glCullFace(GL_FRONT) 绘制效率,且可以进入球体内部 只对球体背面进行绘制,这样提高了一倍的