游戏人生
首页
(current)
GameDevTools
登陆
|
注册
个人中心
注销
游戏引擎 浅入浅出
Introduction
Introduction
前言
前言
1. 游戏引擎框架介绍
1. 游戏引擎框架介绍
1.1 Unity的组成
1.2 游戏引擎组成
2. Opengl开发环境搭建
2. Opengl开发环境搭建
2.1 Opengl到底是什么
2.2 搭建Opengl开发环境
2.3 使用VisualStudio开发
3. 绘制多边形
3. 绘制多边形
3.1 画个三角形
3.2 画个正方形
3.3 画个立方体
4. 着色器
4. 着色器
4.1 Unity Shader和OpenGL Shader
4.2 顶点着色器
4.3 片段着色器
5. 绘制贴图
5. 绘制贴图
5.1 颜色和贴图
5.2 贴图文件介绍
5.3 CPU与GPU的通信方式
5.4 使用stb_image解析图片
5.5 绘制带贴图的立方体盒子
5.6 压缩纹理
5.7 图片压缩工具
5.8 使用压缩纹理
5.9 DXT压缩纹理扩展
6. 索引与缓冲区对象
6. 索引与缓冲区对象
6.1 顶点索引
6.2 缓冲区对象
6.3 OpenGL Core Profile
6.4 顶点数组对象
7. 绘制Mesh和材质
7. 绘制Mesh和材质
7.1 导出Mesh文件
7.2 使用Mesh文件
7.3 Shader文件创建与使用
7.4 创建材质
7.5 使用材质
7.6 MeshRenderer
8. 绘制静态模型
8. 绘制静态模型
8.1 Blender安装与配置
8.2 Blender制作模型
8.3 Blender Python设置开发环境
8.4 Blender Python创建物体
8.5 Blender Python导出顶点数据
8.6 加载导出的Mesh
9. 基于组件开发
9. 基于组件开发
9.1 基于RTTR实现反射
9.2 实现GameObject-Component
10. 相机
10. 相机
10.1 最简单的相机
10.2 多相机渲染
10.3 相机排序
10.4 CullingMask
11. 控制系统
11. 控制系统
11.1 键盘控制
11.2 鼠标控制
12. 拆分引擎和项目
12. 拆分引擎和项目
13. 绘制文字
13. 绘制文字
13.1 TrueType简介
13.2 绘制单个字符
13.3 绘制多个文字
13.4 彩色字
14. GUI
14. GUI
14.1 正交相机
14.2 UIImage
14.3 UIMask
14.4 UIText
14.5 UIButton
15. 播放音效
15. 播放音效
15.1 播放2D音效
15.2 播放3D音效
15.3 使用FMOD Studio音频引擎
16. Profiler
16. Profiler
16.1 初识easy_profiler
16.2 集成easy_profiler
17. 嵌入Lua
17. 嵌入Lua
17.1 Sol2与C++交互
17.2 更加友好的Lua框架设计
17.3 引擎集成sol2
17.4 调试Lua
18. 骨骼动画
18. 骨骼动画
18.1 Blender制作骨骼动画
18.2 Blender导出骨骼动画
18.3 解析骨骼动画
18.4 矩阵的主序
19. 骨骼蒙皮动画
19. 骨骼蒙皮动画
19.1 骨骼蒙皮动画实现
19.2 骨骼权重
19.3 Blender蒙皮刷权重
19.4 Blender导出蒙皮权重
19.5 加载权重文件
20. 解析FBX文件
20. 解析FBX文件
20.1 导出Mesh
20.2 导出骨骼动画
20.3 导出权重
20.4 渲染骨骼蒙皮动画
21. 多线程渲染
21. 多线程渲染
21.1 GLFW多线程渲染
21.2 基于任务队列的多线程渲染
21.3 完全异步的多线程模型
21.4 引擎支持多线程渲染
22. Physx物理引擎
22. Physx物理引擎
22.1 Physx实例-小球掉落
22.2 物理材质
22.3 碰撞检测
22.4 连续碰撞检测
22.5 场景查询
22.6 引擎集成Physx
23. 经典光照
23. 经典光照
23.1 环境光
23.2 漫反射光照模型
23.3 镜面高光光照模型
23.4 高光贴图
23.5 Shader结构体
23.6 Uniform Buffer Object
23.7 方向光
23.8 点光源
23.9 多光源
24. 引擎编辑器的实现
24. 引擎编辑器的实现
24.1 分析Godot引擎编辑器
24.2 FBO RenderTexture GameTurbo DLSS
24.3 ImGui介绍与使用
24.4 分离引擎核心层和应用层
24.5 使用ImGui实现引擎编辑器
24.6 Hierarchy与Inspector面板
24.7 Geometry Buffer
25. Shadow Mapping
25. Shadow Mapping
25.1 深度图
25.2 简单阴影
88. VSCode扩展开发与定制
88. VSCode扩展开发与定制
88.1 第一个VSCode扩展程序
88.2 从源码编译VSCode
88.3 打包VSCode内置扩展
88.4 打包LuaHelper到Code-OSS
89. Doxygen生成API文档
89. Doxygen生成API文档
90. GPU分析工具
90. GPU分析工具
90.1 RenderDoc分析不显示bug
98. SubstancePainter插件开发
98. SubstancePainter插件开发
98.1 SP插件开发环境
98.2 开发SP功能性插件
98.3 开发SP渲染插件
99. Toolbag插件开发
99. Toolbag插件开发
99.1 插件开发环境
99.2 API介绍
99.3 命令行调用Toolbag
99.4 更多实现
99.5 代码参考
附录1. Wwise音频引擎
附录1. Wwise音频引擎
1.1 Wwise名词概念
1.2 Wwise制作音效导出SoundBank
1.3 集成Wwise
1.4 封装Wwise播放3D音效
1.5 Wwise性能分析器介绍
1.6 猎人开发后记
代码资源下载
点我下载
Github
点赞、收藏、关注
目录
<< 25.1 深度图
88. VSCode扩展开发与定制 >>
## 25.2 简单阴影 ```text 「游戏引擎 浅入浅出」是一本开源电子书,PDF/随书代码/资源下载: https://github.com/ThisisGame/cpp-game-engine-book ``` 在OpenGL中获取DepthTexture很简单,只需要将场景渲染到FBO,并且设置DepthTarget为Texture。 但是实际想去使用它,却颇为困难。 还是先看下本小节实现的效果吧,Alt+鼠标左键旋转,可以看到飞机投影到后面的墙壁上。 ![](md/cpp-game-engine-book/imgs/shadow_mapping/simple_shadow/simple_shadow_bias.gif) ### 1. FBO Stack 在上一节,将整个场景渲染到FBO,然后在ImGUI中显示出来DepthTexture,似乎很简单,这是只使用1个FBO的情况。 但这一节,我们使用Shadow Mapping,来实现阴影。 首先需要将场景渲染到FBO,得到DepthTexture,这个FBO先称之为FBO_DepthTexture。 然后再正常渲染一次场景到FBO,用于在Editor中显示,这个FBO称之为FBO_Engine。 那么如何去管理这多个FBO呢? 我们知道FBO其实是个虚拟的屏幕,当Bind一个FBO,后续所有的Draw结果都存储到了这个FBO里。 所以要按照先使用,先Bind的规则,依次对FBO进行Bind操作,然后再取消Bind。 先进先出,所以用Stack来存储。 两个FBO的使用规则如下图: ![](md/cpp-game-engine-book/imgs/shadow_mapping/simple_shadow/multi_fbo_sequence.jpg) 主要代码如下: ```c++ //file:source/render_device/render_target_stack.h /// 渲染目标栈 class RenderTargetStack { public: RenderTargetStack(); ~RenderTargetStack(); /// 压入渲染目标 /// \param frame_buffer_object_id 帧缓冲 void Push(GLuint frame_buffer_object_id); /// 弹出渲染目标 void Pop(); /// 检查是否为空 /// \return 是否为空 bool Empty(); /// 获取栈顶渲染目标 /// \return 栈顶渲染目标 GLuint Top(); private: std::stack<GLuint> fbo_stack_;// 帧缓冲栈 }; ``` 压入FBO_Engine ```c++ //file:source/render_device/render_task_consumer_editor.cpp line:77 void RenderTaskConsumerEditor::InitGraphicsLibraryFramework() { ...... //压入渲染目标栈 render_target_stack_.Push(frame_buffer_object_id); } ``` Bind并压入FBO_DepthTexture ```c++ //file:source/render_device/render_task_consumer_base.cpp line:459 /// 绑定使用FBO任务 void RenderTaskConsumerBase::BindFBO(RenderTaskBase* task_base){ RenderTaskBindFBO* task=dynamic_cast<RenderTaskBindFBO*>(task_base); GLuint frame_buffer_object_id = GPUResourceMapper::GetFBO(task->fbo_handle_); glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer_object_id);__CHECK_GL_ERROR__ //检测帧缓冲区完整性,如果完整的话就开始进行绘制 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);__CHECK_GL_ERROR__ if (status != GL_FRAMEBUFFER_COMPLETE) { DEBUG_LOG_ERROR("BindFBO FBO Error,Status:{} !",status); return; } //压入渲染目标栈 render_target_stack_.Push(frame_buffer_object_id); } ``` UnBind操作,Pop然后检查是否有下一个,有就Bind ```c++ //file:source/render_device/render_task_consumer_base.cpp line:476 /// 取消使用FBO任务 void RenderTaskConsumerBase::UnBindFBO(RenderTaskBase* task_base){ RenderTaskBindFBO* task=dynamic_cast<RenderTaskBindFBO*>(task_base); //弹出渲染目标栈 render_target_stack_.Pop(); //检查是否还有渲染目标 if(render_target_stack_.Empty()){ //如果没有渲染目标了,就使用默认的渲染目标 glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);__CHECK_GL_ERROR__ }else{ //如果还有渲染目标,就使用栈顶的渲染目标 GLuint frame_buffer_object_id = render_target_stack_.Top(); glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer_object_id);__CHECK_GL_ERROR__ //检测帧缓冲区完整性 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);__CHECK_GL_ERROR__ if (status != GL_FRAMEBUFFER_COMPLETE) { DEBUG_LOG_ERROR("UnBindFBO,BindFBO FBO Error,Status:{} !",status); return; } } } ``` ### 2. 实现阴影 光线从光源发出后,被离光源近的物体遮挡,后面的物体受不到光,就显示为阴影。 我们在光源的位置放一个Camera,就叫它DepthCamera,对场景进行渲染,得到DepthTexture。 DepthTexture是一个二维数组,里面记录着离Camera最近的片段的距离。 当换成普通相机,进行普通渲染时,将当前渲染的片段与DepthCamera距离,与DepthTexture中记录的距离比较。 如果小,说明当前片段没有处在阴影中。 那么需要对现有的Shader做一些改变。 顶点Shader:<a id="antiCollectorAdTxt" href="https://github.com/ThisisGame/cpp-game-engine-book">「游戏引擎 浅入浅出」是一本开源电子书,PDF/随书代码/资源下载: https://github.com/ThisisGame/cpp-game-engine-book</a> ```glsl //file:data/shader/unlit_receive_shadow.vert #version 330 core uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform mat4 u_shadow_camera_view; uniform mat4 u_shadow_camera_projection; layout(location = 0) in vec3 a_pos; layout(location = 1) in vec4 a_color; layout(location = 2) in vec2 a_uv; out vec4 v_color; out vec2 v_uv; out vec4 v_shadow_camera_gl_Position; void main() { gl_Position = u_projection * u_view * u_model * vec4(a_pos, 1.0); v_color = a_color; v_uv = a_uv; v_shadow_camera_gl_Position=u_shadow_camera_projection * u_shadow_camera_view * u_model * vec4(a_pos, 1.0); } ``` 因为要将当前渲染片段和DepthTexture中记录的距离进行比较,而DepthTexture是DepthCamera拍下来的,所以需要获取当前片段在DepthCamera空间下的位置 `v_shadow_camera_gl_Position`。 片段Shader: ```glsl //file:data/shader/unlit_receive_shadow.frag #version 330 core uniform sampler2D u_depth_texture; uniform sampler2D u_diffuse_texture; in vec4 v_color; in vec2 v_uv; in vec4 v_shadow_camera_gl_Position; layout(location = 0) out vec4 o_fragColor; float ShadowCalculation(vec4 shadow_camera_gl_Position) { //首先,将传入参数中的x,y,z三个分量除以第四个分量w,得到投影坐标proj_coords。 vec3 proj_coords = shadow_camera_gl_Position.xyz / shadow_camera_gl_Position.w; //将投影坐标映射到范围[0,1] proj_coords = proj_coords * 0.5 + 0.5; //从深度图纹理(u_depth_texture)中提取最近的深度closest_depth,这个深度代表着从光源位置到当前像素的最短距离。 float closest_depth = texture(u_depth_texture, proj_coords.xy).r; //获取当前像素的深度current_depth,即当前像素与光源之间的距离 float current_depth = proj_coords.z; //如果这个距离大于最近距离closest_depth,则说明当前像素在阴影之中,返回1.0,否则返回0.0,代表没有阴影。 float shadow = current_depth > closest_depth ? 1.0 : 0.0; return shadow; } void main() { float shadow = ShadowCalculation(v_shadow_camera_gl_Position); o_fragColor = texture(u_diffuse_texture,v_uv) * v_color * (1-shadow); } ``` 创建DepthCamera的代码如下: ```lua --file:example/login_scene.lua line:77 --- 创建深度相机 function LoginScene:CreateDepthCamera() --创建相机1 GameObject self.go_depth_camera_= GameObject.new("depth_camera") --挂上 Transform 组件 self.go_depth_camera_:AddComponent(Transform):set_position(glm.vec3(0, 0, 10)) self.go_depth_camera_:GetComponent(Transform):set_rotation(glm.vec3(0, 0, 0)) --挂上 Camera 组件 self.depth_camera_=self.go_depth_camera_:AddComponent(Camera) --设置为黑色背景 self.depth_camera_:set_clear_color(0,0,0,1) self.depth_camera_:set_depth(0) self.depth_camera_:SetView(glm.vec3(0.0,0.0,0.0), glm.vec3(0.0,1.0,0.0)) self.depth_camera_:SetPerspective(60, Screen.aspect_ratio(), 1, 1000) --设置RenderTexture self.depth_render_texture_ = RenderTexture.new() self.depth_render_texture_:Init(480,320) self.depth_camera_:set_target_render_texture(self.depth_render_texture_) end ``` 然后需要给顶点Shader传入DepthCamera的投影、视图矩阵。 ```lua --file:example/login_scene.lua line:192 function LoginScene:Update() ...... --设置ShadowCamera的参数 self.material_wall_:SetUniformMatrix4f("u_shadow_camera_view",self.depth_camera_:view_mat4()) self.material_wall_:SetUniformMatrix4f("u_shadow_camera_projection",self.depth_camera_:projection_mat4()) ...... end ``` ### 3. 测试 运行后效果如下图 ![](md/cpp-game-engine-book/imgs/shadow_mapping/simple_shadow/simple_shadow.gif) Alt+鼠标左键旋转,可以看到飞机投影到后面的墙壁上。 ### 4. 阴影失真 上面使用了最简单的方式来实现阴影,太过简陋以至于有很多问题。 最明显的就是旋转相机后,后面的墙壁就出现了各种黑线、甚至黑块,一般称这种情况为**阴影失真(Shadow Acne)**。 这是因为DepthCamera在渲染的时候并没有将墙壁排除在外,DepthTexture中也包含了墙壁的深度。 这就成了自己和自己比较。 解决方法就是添加一个 **阴影偏移(shadow bias)**。 ```glsl //file:data/shader/unlit_receive_shadow.frag ...... float ShadowCalculation(vec4 shadow_camera_gl_Position) { ...... //使用偏移 float bias = 0.005; //如果这个距离大于最近距离closest_depth,则说明当前像素在阴影之中,返回1.0,否则返回0.0,代表没有阴影。 float shadow = current_depth - bias > closest_depth ? 1.0 : 0.0; return shadow; } ...... ``` 最终效果: ![](md/cpp-game-engine-book/imgs/shadow_mapping/simple_shadow/simple_shadow_bias.gif)
<< 25.1 深度图
88. VSCode扩展开发与定制 >>
12
代码资源下载
点我下载
Github
点赞、收藏、关注
目录
Introduction
Introduction
前言
前言
1. 游戏引擎框架介绍
1. 游戏引擎框架介绍
1.1 Unity的组成
1.2 游戏引擎组成
2. Opengl开发环境搭建
2. Opengl开发环境搭建
2.1 Opengl到底是什么
2.2 搭建Opengl开发环境
2.3 使用VisualStudio开发
3. 绘制多边形
3. 绘制多边形
3.1 画个三角形
3.2 画个正方形
3.3 画个立方体
4. 着色器
4. 着色器
4.1 Unity Shader和OpenGL Shader
4.2 顶点着色器
4.3 片段着色器
5. 绘制贴图
5. 绘制贴图
5.1 颜色和贴图
5.2 贴图文件介绍
5.3 CPU与GPU的通信方式
5.4 使用stb_image解析图片
5.5 绘制带贴图的立方体盒子
5.6 压缩纹理
5.7 图片压缩工具
5.8 使用压缩纹理
5.9 DXT压缩纹理扩展
6. 索引与缓冲区对象
6. 索引与缓冲区对象
6.1 顶点索引
6.2 缓冲区对象
6.3 OpenGL Core Profile
6.4 顶点数组对象
7. 绘制Mesh和材质
7. 绘制Mesh和材质
7.1 导出Mesh文件
7.2 使用Mesh文件
7.3 Shader文件创建与使用
7.4 创建材质
7.5 使用材质
7.6 MeshRenderer
8. 绘制静态模型
8. 绘制静态模型
8.1 Blender安装与配置
8.2 Blender制作模型
8.3 Blender Python设置开发环境
8.4 Blender Python创建物体
8.5 Blender Python导出顶点数据
8.6 加载导出的Mesh
9. 基于组件开发
9. 基于组件开发
9.1 基于RTTR实现反射
9.2 实现GameObject-Component
10. 相机
10. 相机
10.1 最简单的相机
10.2 多相机渲染
10.3 相机排序
10.4 CullingMask
11. 控制系统
11. 控制系统
11.1 键盘控制
11.2 鼠标控制
12. 拆分引擎和项目
12. 拆分引擎和项目
13. 绘制文字
13. 绘制文字
13.1 TrueType简介
13.2 绘制单个字符
13.3 绘制多个文字
13.4 彩色字
14. GUI
14. GUI
14.1 正交相机
14.2 UIImage
14.3 UIMask
14.4 UIText
14.5 UIButton
15. 播放音效
15. 播放音效
15.1 播放2D音效
15.2 播放3D音效
15.3 使用FMOD Studio音频引擎
16. Profiler
16. Profiler
16.1 初识easy_profiler
16.2 集成easy_profiler
17. 嵌入Lua
17. 嵌入Lua
17.1 Sol2与C++交互
17.2 更加友好的Lua框架设计
17.3 引擎集成sol2
17.4 调试Lua
18. 骨骼动画
18. 骨骼动画
18.1 Blender制作骨骼动画
18.2 Blender导出骨骼动画
18.3 解析骨骼动画
18.4 矩阵的主序
19. 骨骼蒙皮动画
19. 骨骼蒙皮动画
19.1 骨骼蒙皮动画实现
19.2 骨骼权重
19.3 Blender蒙皮刷权重
19.4 Blender导出蒙皮权重
19.5 加载权重文件
20. 解析FBX文件
20. 解析FBX文件
20.1 导出Mesh
20.2 导出骨骼动画
20.3 导出权重
20.4 渲染骨骼蒙皮动画
21. 多线程渲染
21. 多线程渲染
21.1 GLFW多线程渲染
21.2 基于任务队列的多线程渲染
21.3 完全异步的多线程模型
21.4 引擎支持多线程渲染
22. Physx物理引擎
22. Physx物理引擎
22.1 Physx实例-小球掉落
22.2 物理材质
22.3 碰撞检测
22.4 连续碰撞检测
22.5 场景查询
22.6 引擎集成Physx
23. 经典光照
23. 经典光照
23.1 环境光
23.2 漫反射光照模型
23.3 镜面高光光照模型
23.4 高光贴图
23.5 Shader结构体
23.6 Uniform Buffer Object
23.7 方向光
23.8 点光源
23.9 多光源
24. 引擎编辑器的实现
24. 引擎编辑器的实现
24.1 分析Godot引擎编辑器
24.2 FBO RenderTexture GameTurbo DLSS
24.3 ImGui介绍与使用
24.4 分离引擎核心层和应用层
24.5 使用ImGui实现引擎编辑器
24.6 Hierarchy与Inspector面板
24.7 Geometry Buffer
25. Shadow Mapping
25. Shadow Mapping
25.1 深度图
25.2 简单阴影
88. VSCode扩展开发与定制
88. VSCode扩展开发与定制
88.1 第一个VSCode扩展程序
88.2 从源码编译VSCode
88.3 打包VSCode内置扩展
88.4 打包LuaHelper到Code-OSS
89. Doxygen生成API文档
89. Doxygen生成API文档
90. GPU分析工具
90. GPU分析工具
90.1 RenderDoc分析不显示bug
98. SubstancePainter插件开发
98. SubstancePainter插件开发
98.1 SP插件开发环境
98.2 开发SP功能性插件
98.3 开发SP渲染插件
99. Toolbag插件开发
99. Toolbag插件开发
99.1 插件开发环境
99.2 API介绍
99.3 命令行调用Toolbag
99.4 更多实现
99.5 代码参考
附录1. Wwise音频引擎
附录1. Wwise音频引擎
1.1 Wwise名词概念
1.2 Wwise制作音效导出SoundBank
1.3 集成Wwise
1.4 封装Wwise播放3D音效
1.5 Wwise性能分析器介绍
1.6 猎人开发后记