当前位置:   article > 正文

unity 天空盒_Unity自定义可编程渲染管线(SRP)(二)——编写第一个自定义SRP

public void setupcameraproperties(camera camera, bool stereosetup = false);

0cfc7abdfe00ac5e122bbb0779c2c1c9.png

一句话描述,我们可以把SRP分解成两个部分,分别是SRP Asset,SRP Instance。

SRP Asset

SRP Asset是一个Unity Asset文件,用来存储渲染管线的特定配置信息,包含的信息有:游戏物体是否应该投射阴影;使用什么样的着色器质量级别;影子距离;默认材质配置。

此外还可以保存所有像在配置中保存和控制的信息和unity需要序列化的数据。SRP Asset定义了SRP的类型和所有配置数据的设置。

SRP Instance

SRP Instance是实际执行渲染的类,当unity发现工程使用SRP的时候,就会寻找当前使用的SRP Asset并请求一个渲染实例,该实例必须包含一个Render函数,

渲染实例表示管道配置。在渲染调用中,Unity可以执行如下操作:

  • 清理framebuffer。
  • 执行场景清理。
  • 渲染游戏对象集。
  • 从一个帧缓冲区到另一个帧缓冲区执行位块传输;
  • 渲染阴影。
  • 应用后处理效果。

第一个SRP Asset案例

SRP Asset实际是一个继承RenderPipelineAsset接口的类,需要实现CreatePipeline接口。当unity首次执行渲染命令的时候,则会调用这个接口并返回一个可用的渲染实例。

下面所展示的就是一个简单的SRP Asset类。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Rendering;
  5. [CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
  6. public class CustomRenderPipelineAsset : RenderPipelineAsset
  7. {
  8. protected override RenderPipeline CreatePipeline()
  9. {
  10. return new CustomRenderPipeline();
  11. }
  12. }

SRP Instance

在上面的叙述中,CustomRenderPipelineAsset类重写了CreatePipeline方法,并返回了一个CustomRenderPipeline实例对象,该对象则是unity SRP渲染的入口。CustomRenderPipeline是一个继承RenderPipeline的类,而且必须实现虚函数Render,该函数包含了两个参数:

ScriptableRenderContext context:是一个command buffer对象,可以向其输入您想执行的渲染指令。

Camera[] cameras:一系列用于渲染的相机。

先介绍一个简单的例子:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Rendering;
  5. public class CustomRenderPipeline : RenderPipeline
  6. {
  7. public CameraRender cameraRender = new CameraRender();
  8. public CustomRenderPipeline()
  9. {
  10. }
  11. protected override void Render(ScriptableRenderContext context, Camera[] cameras)
  12. {
  13. foreach(var camera in cameras)
  14. {
  15. cameraRender.Render(context, camera);
  16. }
  17. }
  18. }

如上所示,我们把渲染的处理逻辑封装到CameraRender类里,对每一个场景中的活动相机进行单独处理。

下一步先定义CameraRender类:

  1. using UnityEngine;
  2. using UnityEngine.Rendering;
  3. public class CameraRenderer {
  4. ScriptableRenderContext context;
  5. Camera camera;
  6. public void Render (ScriptableRenderContext context, Camera camera) {
  7. this.context = context;
  8. this.camera = camera;
  9. }
  10. }

然后正式定义Render函数,实现我们的自定义渲染管线。

  1. 绘制天空盒

在Render函数中是要绘制所有Camera可见的几何体,我们声明一个函数DrawVisibleGeometry用于处理一些特殊的工作,绘制天空盒也是在这个方法中处理,不过我们还另外声明了一个方法来完成这个工作。代码如下:

  1. public void Render (ScriptableRenderContext context, Camera camera) {
  2. this.context = context;
  3. this.camera = camera;
  4. DrawVisibleGeometry();
  5. }
  6. void DrawVisibleGeometry () {
  7. context.DrawSkybox(camera);
  8. }

然而这样仍不能显示天空盒,这是因为我们调用DrawSkybox只是把命令添加到context的缓存队列里,还需要调用context.Submit()方法才会正式执行缓存队列里面的命令。

  1. public void Render (ScriptableRenderContext context, Camera camera) {
  2. this.context = context;
  3. this.camera = camera;
  4. DrawVisibleGeometry();
  5. Submit();
  6. }
  7. void Submit () {
  8. context.Submit();
  9. }

最后我们可以看到下面的效果。

6cd63f06b9b8c0ada6d54f3b78ccb333.png

但是,这个渲染结果还是有问题,当我们调整相机的角度或位置时,渲染结果并没有发生变化,这是因为我们并没有将视角投影矩阵传递给Command Buffer。这个变换矩阵在Unity Shader中是unity_MatrixVP。当我们打开frame debugger面板,选择一个draw call,这个变量就会显示在右侧的监视板上。当前,不管我们怎么调整相机的姿态,这个变量都会保持不变,通过调用SetupCameraProperties方法可以构建这个矩阵。如下所示

cb9dcd15a71acc6656d1155ff16b299a.png

但是,这个渲染结果还是有问题,当我们调整相机的角度或位置时,渲染结果并没有发生变化,这是因为我们并没有将视角投影矩阵传递给Command Buffer。这个变换矩阵在Unity Shader中是unity_MatrixVP。当我们打开frame debugger面板,选择一个draw call,这个变量就会显示在右侧的监视板上。当前,不管我们怎么调整相机的姿态,这个变量都会保持不变,通过调用SetupCameraProperties方法可以构建这个矩阵。如下所示

  1. public void Render (ScriptableRenderContext context, Camera camera) {
  2. this.context = context;
  3. this.camera = camera;
  4. Setup();
  5. DrawVisibleGeometry();
  6. Submit();
  7. }
  8. void Setup () {
  9. context.SetupCameraProperties(camera);
  10. }

2. Command buffer

前面已经提到,context只有在调用Submit方法时才会执行渲染指令,在这之前都是将指令添加到渲染队列中。像天空盒可以通过专业的方法添加指令,但是像其他任务则不能通过这样添加,需要通过一个单独的command buffer间接添加。因此我们需要另建一个buffer用来绘制其他的几何对象。

  1. const string bufferName = "Render Camera";
  2. CommandBuffer buffer = new CommandBuffer {
  3. name = bufferName
  4. };

在项目开发中,profiler是一个非常强大和有用的工具,这里我们可以把我们的Command Buffer加入到profiler样例中,这个样例可以同时显示在profiler和frame debugger面板,方法是在适当的位置调用BeginSample和EndSample方法,在我们的案例中将会分别放在Setup()方法和Submit()方法的开头位置。BeginSample和EndSample方法必须使用相同的字符串参数作为样例的名字,这里使用的是buffer的名字。

  1. void Setup () {
  2. buffer.BeginSample(bufferName);
  3. context.SetupCameraProperties(camera);
  4. }
  5. void Submit () {
  6. buffer.EndSample(bufferName);
  7. context.Submit();
  8. }

执行Command Buffer中的命令只需要调用context的ExecuteCommandBuffer方法,并把buffer作为参数传递进去。

  1. void Setup () {
  2. buffer.BeginSample(bufferName);
  3. ExecuteBuffer();
  4. context.SetupCameraProperties(camera);
  5. }
  6. void Submit () {
  7. buffer.EndSample(bufferName);
  8. ExecuteBuffer();
  9. context.Submit();
  10. }
  11. void ExecuteBuffer () {
  12. context.ExecuteCommandBuffer(buffer);
  13. buffer.Clear();
  14. }

这时候打开frame debugger我们就可以看到渲染的案例如下:

8c108b9326c96f13fcd32d1f7002d011.png

3. 清除渲染目标

不管我们想要绘制什么东西,最终都会渲染到相机的渲染目标中。相机的渲染目标一般是帧缓存区,也可以是渲染纹理。渲染目标一般不会自动清除,也就是说之前渲染的结果会一直保存在渲染目标中,这样会干扰我们渲染新的图像。为了保证正确的渲染,我们必须清除渲染目标,以摆脱其旧的内容。通过调用ClearRenderTarget方法可以实现这个目的。

ClearRenderTarget函数需要至少三个参数,前两个参数指定是否清理颜色缓存和深度缓存,一般都设为true,第三个参数指定清理缓存的颜色,这里使用Color.clear。

  1. void Setup () {
  2. buffer.BeginSample(bufferName);
  3. buffer.ClearRenderTarget(true, true, Color.clear);
  4. ExecuteBuffer();
  5. context.SetupCameraProperties(camera);
  6. }

4d4386483eb9207885c5cb813dd2804a.png

打开frame debugger,可以看到清理渲染目标活动对应了一个叫DrawGL的样例。而且这个案例被镶嵌在另一个Render Camera样例中,这是因为 ClearRenderTarget将清除任务包装在了一个使用Command buffer的名字命名的样例中。为了避免这种情况,可以调整一下清除目标的代码的位置。

  1. void Setup () {
  2. context.SetupCameraProperties(camera);
  3. buffer.ClearRenderTarget(true, true, Color.clear);
  4. buffer.BeginSample(bufferName);
  5. ExecuteBuffer();
  6. //context.SetupCameraProperties(camera);
  7. }

016d751bd45377e6cffc890a04e580f3.png

现在我们可以看到Clear(color+Z+stencil),这表明颜色跟深度缓冲都没清除了,stencil数据是同一缓存区的一部分。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/102773
推荐阅读
相关标签
  

闽ICP备14008679号