2、unity环境部署
确保版本要正确,文中使用:Unity 2018.3.0f2
uity下载地址:https://unity3d.com/get-unity/download/archive
注意点:不要先用低版本打开,会生成cache文件,需要直接解压一份新的源码,然后用上面的版本的unity打开即可。
否则会出现报错,导致自定义的管线资源无法正确加载。
3、如何创建一个自定义的管线资源
这步后面再详细的叙述,现在我们已经有了一个管线的资源了。
4、如何让unity使用我们自己的管线
将自定义的管线资源拖拽到Project Settings-》Graphics-》Scriptable Render Pipeline Settings5、clip像素操作
打开场景:Shadow Scene,定位到这个部分的物体:
这几个物体就是clip操作之后的效果,有镂空的效果。
剔除函数:
float4 albedoAlpha = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
albedoAlpha *= UNITY_ACCESS_INSTANCED_PROP(PerInstance, _Color);
#if defined(_CLIPPING_ON)
clip(albedoAlpha.a - _Cutoff);
#endif
这是最核心的地方,然后我们再来仔细介绍下。SAMPLE_TEXTURE2D这个宏在GLCore.hlsl中定义如下:
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
textureName纹理名称
samplerName采样器名称
coord2二维纹理坐标纹理是资源,采样器是手段,他们的声明如下:
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex); //sampler在纹理前面加上sampler两个宏分别如下:
#define TEXTURE2D(textureName) Texture2D textureName
#define SAMPLER(samplerName) SamplerState samplerNameUNITY_ACCESS_INSTANCED_PROP
#define UNITY_ACCESS_INSTANCED_PROP(arr, var) arr##Array[unity_InstanceID].varUNITY_INSTANCING_BUFFER_START(PerInstance)
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
UNITY_INSTANCING_BUFFER_END(PerInstance)
宏定义如下:
#define UNITY_INSTANCING_BUFFER_START(buf) UNITY_INSTANCING_CBUFFER_SCOPE_BEGIN(UnityInstancing_##buf) struct {
#define UNITY_INSTANCING_BUFFER_END(arr) } arr##Array[UNITY_INSTANCED_ARRAY_SIZE]; UNITY_INSTANCING_CBUFFER_SCOPE_END
#define UNITY_DEFINE_INSTANCED_PROP(type, var) type var;
#define UNITY_ACCESS_INSTANCED_PROP(arr, var) arr##Array[unity_InstanceID].var
6、内表面的法线反转
这个前提是内表面也参与渲染,使用cull off
我们先说如果不反转的话,错误的结果是怎样的,然后看看反转了之后,正确的结果是什么样的?
左图是内部法线没有反转的情况,内部区域是黑色的,没有照亮的;而右侧是是内部法线反转了之后的情况,内部是被照亮了的,很显然右边渲染的结果正确。因为光线透过了洞,照进了球体的内表面。当然我们还能看到有些是黑色的,因为是影子的结果。
下面再介绍如何反转内表面的法线:
input.normal = IS_FRONT_VFACE(isFrontFace, input.normal, -input.normal);
IS_FRONT_VFACE是内置的宏,#define IS_FRONT_VFACE(VAL, FRONT, BACK) ((VAL > 0.0) ? (FRONT) : (BACK))
7、双面渲染
首先介绍单面渲染,unity默认的球体是只有单面材质的,如下:
上面的图,是关闭cull off,所以正面和背面都被渲染。
如果是不画背面:cuff back
如果是不画正面:cull front
所以,如果想,让内部的面,也照亮,我们需要将背面和正面一起画出来。也就是cull off,那么对于背面此时要进行法线的反转:
这是正确的结果,再来总结下,对于clip的情况,需要cull off,然后对内部表面进行法线的反转。所谓的双面显然,其实就是通过cull off实现,渲染了front+back的两个面。
8、clip像素的渲染队列
clip的渲染队列,我们一般放在alphatest中,原因如下:
参考:https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/transparency/ 1.8节。
besides potentially discarding framents, alpha-clipped rendering works the same as opaque rendering and both can be mixed without issue. but because alpha clipping prevents some gpu optimizations it is typical to first render all purely opaque objects before rendering all alpha-clipped objects. that enables the most optimization, potentially limits the amout of alpha-clipped fragments as more end up hidden behind opaque geometry, and can also reduce the amount of batches. all this can be done by simply setting the alpha-clipped materials to use a later render queue. the defaut queue for alpha-clipped material is 2450, cooresponding to the Alpha Test.
9、transparent的渲染队列
透明和半透明,其实是一回事,就是涉及到混合,我们要将透明的物体最后画。然后和之前画的物体的颜色进行混合。
所以第一步要打开混合:
Blend [_SrcBlend] [_DstBlend]
默认的是:Blend Off
所以要打开混合,使用使用:Blend SrcAlpha OneMinusSrcAlpha 这个一个是打开了混合,另外还声明如何进行混合,一举两得。
https://docs.unity3d.com/Manual/SL-Blend.html 透明的渲染队列,设置为:Transparent,其值为3000,很靠后了。
10、为啥透明渲染的时候要关闭深度写入
semi-transparency now sometimes works as it should, but also produces weird results. this is especially noticeable because we are still castint shadows as if the surfaces were opaque. this because we are not culling, so both sides of the surfaces get rendered. which part gets rendered first depends on the triangle order of the mesh. when a front-facing triangle gets rendered first, there is not a back side to blend with yet. and the back will not get rendered because it is behind sth. that already got rendered.
the same problem also happens when two separate transparent objects are close to each other. unity sorts transparent objects back-to-front, which is correct but can only consider the object position, not shape. part of an object that is drawn first an still end up in front of an object that gets drawn later. for example, put two mostly-overlapping quads in the scene, one a bit above the other, and adjust the view until the top one gets rendered first.
we can not avoid this except by carefully controlling the placement of semitransparent objects or using materials with different render queues. in case of intersecting objects or a double-sided material with arbitrary triangle order, it will always go wrong. but what we can do is disable writing to the depth buffer for transparent matrials. that way what gets rendered first will never block what gets rendered later.
11、double-sided with semi-transparency
上面介绍了,为何要关闭深度写入。下面介绍在关闭了深度写入之后,并不是万事大吉了,还会有其他的问题。对于双面渲染的透明材质来说,就会出现问题。
with z writing disabled, the insides of objects always get rendered when culling is off. however, the draw order is still determined by the triangle order of the mesh. this is guaranteed to produce incorrect results when using the default sphere and cube.
with an arbitrary mesh the only way to ensure that the back face are drawn first is to duplicate the object and use two materials, one that culls front and another that culls back. then adjust the render queues so that the inside is drawn first.
但是还是有问题
that works for an individual object, but not when multiple such objects are visually overlapping. in that case all outsides gets drawn on top of all insides.
终极解决方法:
the best wayt to render double-sided semi-transparent surfaces is to use a mesh specifically created for this purpose. 为模型内外表面创建两份mesh。the mesh must contain separate triangles for its inside and outside, ordered so that the inside is drawn first. even then, this only reliably works for concave objects that never visually overlap themselves.
u can create a double-sided mesh with a separate 3D modeler, but we can also make a simple tool in unity to quickly generate a double-sided variant of any source mesh.
请参考:https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/transparency/ 2.5节。
对于这个解决方法,在冯乐乐的书中的8.7.2节中也提到解决方法,写一个shader,包括两个pass:一个pass用来渲染背面;一个pass用来渲染正面。由于unity 会顺序执行sub shader的各个pass,因此我们可以保证背面总是在正面被渲染之前渲染,从而可以保证正确的深度渲染关系。
12、Command buffers https://docs.unity3d.com/ScriptReference/Rendering.CommandBuffer.html
list of graphics commands to execute.
command buffers hold list of rendering commands (“set render target, draw mesh,…”). They can be set to execute at various points during camera rendering (see Camera.AddCommandBuffer), light rendering (see Light.AddCommandBuffer) or be executed immediately (see Graphics.ExecuteCommandBuffer).
Typically they would be used to extend Unity’s rendering pipeline in some custom ways. For example, you could render some additional objects into deferred rendering g-buffer after all regular objects are done, or do custom processing of light shadow maps. See command buffers overview page for more details.
Command buffers can be created and then executed many times if needed.
over view:
https://docs.unity3d.com/Manual/GraphicsCommandBuffers.html
command buffers allow u to extend unity’s built-in render pipeline. a command buffer holds a list of rendering commands which execute at various points during camera rendering. to specify a position in unity’s built-in render pipeline for a command buffer to execute, use the cameraEvent.enum.
For example, you can use a Command Buffer with the AfterGBuffer CameraEvent to render additional GameObjects
into the Deferred pipeline, after the pipeline processes all opaque GameObjects.
Below is a high-level overview of how Cameras use the Forward or Deferred pipeline to render a Scene in Unity.
•Black boxes represent an internal Unity process.
•Blue boxes represent a CameraEvent where you can add Command Buffers.
See the CommandBuffer Class and the CameraEvent Enum for more information. You can also use Command buffers in conjunction with, or as a replacement for, image effects.