赞
踩
绘制物体,包括不透明的物体,透明物体,再加上之前的天空盒
Camera
void DrawVisibleGeometry() { //我们需要将不透明物体和透明物体分开绘制 //如果我们直接先绘制所有的物体,然后再绘制天空盒,我们就会看到对于透明的物体,如果其对于相机视线后面没有不透明的物体的话,就会被天空球给遮挡住 //我们的透明shader没有写入深度缓冲,绘制天空盒时,在这个片元上如果没有不透明的物体写入过深度的话,天空盒的绘制就会直接覆盖掉 //所以我们的绘制顺序是先不透明物体,再绘制天空盒,最后绘制透明物体 //先进行不透明物体的绘制 var sortingSettings = new SortingSettings(camera) //相机的透明排序取决于使用的是正交还是基于距离的排序 { // 不给定排序规则的话,绘制排序是随机混乱的 // 这个排序是从前往后的,因为对于后面的不透明物体,当检测到其的深度大于缓冲区的深度时,说明被挡住了,可以直接丢弃不绘制 // 而如果从后往前的话,就会需要不断的覆盖绘制,性能不如从前往后 criteria = SortingCriteria.CommonOpaque, }; var drawSettings = new DrawingSettings(unlitShaderTagId, sortingSettings); //DrawingSettings主要用于对物体的渲染顺序和使用哪个Shader Pass var filteringSettings = new FilteringSettings(RenderQueueRange.opaque); //FilteringSettings主要决定哪些物体渲染,哪些不渲染根据LayerMask,RenderQueue,sortingLayer等 //在Cull中我们得到了cullingResults,因此我们知道了哪些物体是需要绘制的 context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings); context.DrawSkybox(camera); //添加绘制Skybox的命令 // 再进行透明物体的绘制 // 这个绘制顺序是从后往前的,与不透明的绘制顺序是相反的 // 不透明的物体并不写入深度缓冲,因此从前往后并没有性能更优 // 而且透明物体之间需要进行混合,从后往前才能得到正确的混合 // 但事实上并不能完全保证正确,因为排序是基于整个物体的位置的,但是对于复杂的大型物体间,可能会有部分穿插,有部分在前,有部分在后,这样子就会得到错误的效果 sortingSettings.criteria = SortingCriteria.CommonTransparent; drawSettings.sortingSettings = sortingSettings; filteringSettings.renderQueueRange = RenderQueueRange.transparent; context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings); } //使用Unity默认的一些Shader绘制出使用了不支持的Shader的物体 partial void DrawUnsupportedShaders() //如果不改成分部方法的定义和实现声明,打包就会出错,因为这部分代码是仅在编辑器下使用的 { if (errorMaterial == null) { errorMaterial = new Material(Shader.Find("Hidden/InternalErrorShader")); } //我们将不支持的Shader用Unity默认的设置直接绘制出来 //没有设置好Shader的属性之前,绘制出来的物体是黑色的 var drawSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera)) { overrideMaterial = errorMaterial, //使用内置错误材质来绘制所有不支持的Shader的物体 } ; for (int i = 1; i < legacyShaderTagIds.Length; i++) { drawSettings.SetShaderPassName(i, legacyShaderTagIds[i]); } var filteringSettings = FilteringSettings.defaultValue; context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings); }
没有对绘制排序前
排序后
将不透明物体和透明物体分开渲染
先用默认Shader渲染不支持的Shader
用内置的错误材质在编辑器下绘制
分部代码,让代码更清晰,且可以让Editor代码不会出现打包错误
partial void DrawGizmos()
{
if (Handles.ShouldRenderGizmos()) //获取Gizmos是否开启
{
context.DrawGizmos(camera, GizmoSubset.PreImageEffects);
context.DrawGizmos(camera, GizmoSubset.PostImageEffects);
}
}
绘制Gizmos前
绘制Gizmos后
如果是Overlay的UI,则不受我们的管线影响
改成ScreenCamera或者WorldSpaceCamera后
但是Scene中没有UI
添加绘制Scene视图的代码后
partial void PrepareForSceneWindow()
{
if (camera.cameraType == CameraType.SceneView)
{
//绘制Scene视图中的UI
ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
}
}
添加新的相机
两个相机的绘制在FrameDebuger中都混在了一起
我们对每个相机单独进行Sample
partial void PrepareBuffer()
{
//我们将Buffer的名称修改为相机名称,这样子就可以在FrameDebuger中看到相机对应的渲染在自己的组中,查看会比较清晰
Profiler.BeginSample("Editor Only"); //这里会产生GC,会混下我们性能分析,所以直接标明是Editor Only
buffer.name = SampleName = camera.name;
Profiler.EndSample();
}
有多余的GC
把Editor下的GC单独分开
根据相机的ClearFlags的设置去调用buffer.ClearRenderTarget,主要就是决定是否清除颜色缓冲,深度缓冲
var flags = camera.clearFlags;
//ClearRenderTarget需要在SetupCameraProperties之后进行Execute,不然Clear的方式会变成Draw GL(可以在FrameDebuger中看到)
//Draw GL是使用Hidden/InternalClear Shader绘制了一个全屏的Quad来达到清除的效果,这种方式不是性能最好的
//ClearRenderTarget需要在BeginSample之前,不然FrameDebuger中会被多一次嵌套在"Render Camera"中
buffer.ClearRenderTarget(
flags != CameraClearFlags.Nothing,
flags == CameraClearFlags.Color, //为什么可以不用管Skybox时的清理颜色缓冲?因为当选择Skybox时,会清理深度缓冲,且需要绘制Skybox,没有深度缓冲此时一定会覆盖掉之前的颜色,相当于清理了颜色缓冲
flags == CameraClearFlags.Color ? camera.backgroundColor.linear : Color.clear //我们用的是线性空间,所以当要使用背景颜色时,需要将其转化为线性空间的颜色
);
Solid Color
Don’t Clear
改变Viewport后的Skybox
改变Viewport后的SolidColor
改变Viewport后的Depth Only
改变Viewport后的Don’t Clear
没有改变Viewport之前是直接用Clear的方式
改变Viewport后,由于需要将清理的范围限定在Viewport中,并非整个屏幕,因此使用Shader绘制的方式清理,将范围限定
本文主要学习自:https://catlikecoding.com/unity/tutorials/custom-srp/custom-render-pipeline/
catlikecoding是大神的博客,里面有很多教程,膜拜大神,感恩大神。
CustomRenderPipeline.cs
using UnityEngine;
using UnityEngine.Rendering;
public class CustomRenderPipeline : RenderPipeline
{
CameraRenderer cameraRenderer = new CameraRenderer();
//每一帧渲染调用
protected override void Render(ScriptableRenderContext context, Camera[] cameras)
{
foreach(var camera in cameras)
{
cameraRenderer.Render(context, camera);
}
}
}
CustomRenderPipelineAsset.cs
using UnityEngine;
using UnityEngine.Rendering; //需要使用UnityEngine.Rendering命名空间
//用于存储自定义渲染管线的设置
[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset //需要继承自RenderPipelineAsset
{
protected override RenderPipeline CreatePipeline()
{
return new CustomRenderPipeline();
}
}
CameraRenderer.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; //用于进行单个相机的画面渲染 public partial class CameraRenderer { static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit"); ScriptableRenderContext context; Camera camera; CullingResults cullingResults; const string bufferName = "Render Camera"; CommandBuffer buffer = new CommandBuffer { //创建实例时可以这样直接初始化 name = bufferName, }; public void Render(ScriptableRenderContext context, Camera camera) { this.context = context; this.camera = camera; PrepareBuffer(); PrepareForSceneWindow(); if (!Cull()) return; Setup(); DrawVisibleGeometry(); DrawUnsupportedShaders(); DrawGizmos(); Submit(); } bool Cull() { //返回值表示cullingParameters是否有效,比如当相机viewport rectangle宽高被设置为0,0,无效的裁剪平面设置等 //因此返回值能代表这个相机是否需要绘制 if (camera.TryGetCullingParameters(out var cullingParameters)) { //我们会发现在这里,对ScriptableCullingParameters的操作基本都是out和ref,这是因为ScriptableCullingParameters比较大,这样子可以优化 cullingResults = context.Cull(ref cullingParameters); return true; } return false; } void Setup() { //将相机的属性设置到绘制命令中,比如相机的位置和旋转,透视还是正交投影等,这会决定视图矩阵和投影矩阵 //就是Shader中所使用的unity_MatrixVP, View Matrix, Projection Matrix //我们可以在FrameDebuger中的ShaderProperties中看到unity_MatrixVP //如果我们不进行这项设置,unity_MatrixVP都是一样的,所以当我们旋转相机时,相机看到的东西不会发生变化 context.SetupCameraProperties(camera); var flags = camera.clearFlags; //ClearRenderTarget需要在SetupCameraProperties之后进行Execute,不然Clear的方式会变成Draw GL(可以在FrameDebuger中看到) //Draw GL是使用Hidden/InternalClear Shader绘制了一个全屏的Quad来达到清除的效果,这种方式不是性能最好的 //ClearRenderTarget需要在BeginSample之前,不然FrameDebuger中会被多一次嵌套在"Render Camera"中 buffer.ClearRenderTarget( flags != CameraClearFlags.Nothing, flags == CameraClearFlags.Color, //我们的Skybox是在最后绘制的 flags == CameraClearFlags.Color ? camera.backgroundColor.linear : Color.clear //我们用的是线性空间,所以当要使用背景颜色时,需要将其转化为线性空间的颜色 ); buffer.BeginSample(SampleName); ExecuteBuffer(); //Execute后,Buffer中的命令才会加入到context中 } void DrawVisibleGeometry() { //我们需要将不透明物体和透明物体分开绘制 //如果我们直接先绘制所有的物体,然后再绘制天空盒,我们就会看到对于透明的物体,如果其对于相机视线后面没有不透明的物体的话,就会被天空球给遮挡住 //我们的透明shader没有写入深度缓冲,绘制天空盒时,在这个片元上如果没有不透明的物体写入过深度的话,天空盒的绘制就会直接覆盖掉 //所以我们的绘制顺序是先不透明物体,再绘制天空盒,最后绘制透明物体 //先进行不透明物体的绘制 var sortingSettings = new SortingSettings(camera) //相机的透明排序取决于使用的是正交还是基于距离的排序 { // 不给定排序规则的话,绘制排序是随机混乱的 // 这个排序是从前往后的,因为对于后面的不透明物体,当检测到其的深度大于缓冲区的深度时,说明被挡住了,可以直接丢弃不绘制 // 而如果从后往前的话,就会需要不断的覆盖绘制,性能不如从前往后 criteria = SortingCriteria.CommonOpaque, }; var drawSettings = new DrawingSettings(unlitShaderTagId, sortingSettings); //DrawingSettings主要用于对物体的渲染顺序和使用哪个Shader Pass var filteringSettings = new FilteringSettings(RenderQueueRange.opaque); //FilteringSettings主要决定哪些物体渲染,哪些不渲染根据LayerMask,RenderQueue,sortingLayer等 //在Cull中我们得到了cullingResults,因此我们知道了哪些物体是需要绘制的 context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings); context.DrawSkybox(camera); //添加绘制Skybox的命令 // 再进行透明物体的绘制 // 这个绘制顺序是从后往前的,与不透明的绘制顺序是相反的 // 不透明的物体并不写入深度缓冲,因此从前往后并没有性能更优 // 而且透明物体之间需要进行混合,从后往前才能得到正确的混合 // 但事实上并不能完全保证正确,因为排序是基于整个物体的位置的,但是对于复杂的大型物体间,可能会有部分穿插,有部分在前,有部分在后,这样子就会得到错误的效果 sortingSettings.criteria = SortingCriteria.CommonTransparent; drawSettings.sortingSettings = sortingSettings; filteringSettings.renderQueueRange = RenderQueueRange.transparent; context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings); } void Submit() { buffer.EndSample(SampleName); ExecuteBuffer(); context.Submit(); //提交绘制命令,进行绘制 } void ExecuteBuffer() { context.ExecuteCommandBuffer(buffer); buffer.Clear(); } }
CameraRenderer.Editor.cs
using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine.Profiling; using UnityEngine; using UnityEngine.Rendering; //用于进行单个相机的画面渲染 partial class CameraRenderer { //为分部方法定义声明,这样子编译时如果没有找到实现声明的方法的话,就会剔除掉对这个方法的调用 //使用这种方法可以比较好的避免Build出错,再来查错 partial void PrepareBuffer(); partial void PrepareForSceneWindow(); partial void DrawUnsupportedShaders(); partial void DrawGizmos(); #if UNITY_EDITOR string SampleName { get; set; } static Material errorMaterial; //用于绘制不支持的shader //Unity默认的所有的Shader Pass的Tag static ShaderTagId[] legacyShaderTagIds = { new ShaderTagId("Always"), new ShaderTagId("ForwardBase"), new ShaderTagId("PrepassBase"), new ShaderTagId("Vertex"), new ShaderTagId("VertexLMRGBM"), new ShaderTagId("VertexLM"), }; partial void PrepareBuffer() { //我们将Buffer的名称修改为相机名称,这样子就可以在FrameDebuger中看到相机对应的渲染在自己的组中,查看会比较清晰 Profiler.BeginSample("Editor Only"); //这里会产生GC,会混下我们性能分析,所以直接标明是Editor Only buffer.name = SampleName = camera.name; Profiler.EndSample(); } partial void PrepareForSceneWindow() { if (camera.cameraType == CameraType.SceneView) { //绘制Scene视图中的UI ScriptableRenderContext.EmitWorldGeometryForSceneView(camera); } } //使用Unity默认的一些Shader绘制出使用了不支持的Shader的物体 partial void DrawUnsupportedShaders() //如果不改成分部方法的定义和实现声明,打包就会出错,因为这部分代码是仅在编辑器下使用的 { if (errorMaterial == null) { errorMaterial = new Material(Shader.Find("Hidden/InternalErrorShader")); } //我们将不支持的Shader用Unity默认的设置直接绘制出来 //没有设置好Shader的属性之前,绘制出来的物体是黑色的 var drawSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera)) { overrideMaterial = errorMaterial, //使用内置错误材质来绘制所有不支持的Shader的物体 } ; for (int i = 1; i < legacyShaderTagIds.Length; i++) { drawSettings.SetShaderPassName(i, legacyShaderTagIds[i]); } var filteringSettings = FilteringSettings.defaultValue; context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings); } partial void DrawGizmos() { if (Handles.ShouldRenderGizmos()) //获取Gizmos是否开启 { context.DrawGizmos(camera, GizmoSubset.PreImageEffects); context.DrawGizmos(camera, GizmoSubset.PostImageEffects); } } #else const string SampleName = bufferName; #endif }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。