Unity渲染各种注意事项

内部PBR流程

pbr材质的光滑反射度流程
  1. Albedo Texture的rgb通道对应光照运算方程中的diffuse color;a通道对应于Alpha;
  2. Speculer Texture的rgb通道对应光照运算方程的F0;a通道对应光照运算方程的Smoothness;

其oneMinusReflectivity = 1-SpecularStrength(specColor);//SpecularStrength函数是来获取rgb通道最大值;
oneMinusReflectivity 用于计算IBL reflection的菲涅尔项,具体可看源码,但是我不知道算法的原理==;

pbr材质的金属粗糙度流程
  1. Albedo Texture的rgb通道为albedo;a通道对应于Alpha;
    其diffuse color需要通过以下方法来计算:
    oneMinusReflectivity = unity_ColorSpaceDielectricSpec.a*(1-metallic);
    diffColor = albedo * oneMinusReflectivity;
  2. Aetallic Texture的r通道对应金属度metallic;a通道对应光照运算方程的Smoothness;
    其Speculer Color(即F0)需要通过以下方法来计算:
    specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);

注意:unity_ColorSpaceDielectricSpec在unity内部的值为half4(0.04, 0.04, 0.04, 1.0 - 0.04) //linear space

unity内部的environmentBRDF
  1. 对于IBL部分的BRDF,unity好像采用的拟合的方法来进行计算,在unreal中存在LUT与拟合两种方法;
  2. 对于unity采用的拟合方法,其F项计算方法为:
half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
FresnelLerp (specColor, grazingTerm, nv);

其它项为:

    half surfaceReduction;
#   ifdef UNITY_COLORSPACE_GAMMA
        surfaceReduction = 1.0-0.28*roughness*perceptualRoughness;      // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]
#   else
        surfaceReduction = 1.0 / (roughness*roughness + 1.0);           // fade \in [0.5;1]
#   endif
阴影投射通道的编写
  1. 暂时先copy自unity官方代码,在其内部包括了bias、normal bias等参数的处理,即light组件下,Realtime Shadows的那些参数,有空在仔细分析。

渲染流程中的各种问题

MSAA、HDR、_CameraDepthTexture的问题
  1. 在windows平台下,可以直接使用_CameraDepthTexture,并且MSAA与HDR一起开好像也没问题;windows下,查看frame debugger,会发现先渲染_CameraDepthTexture深度图,然后在渲染两张分辨率不同的ShadowMap,然后是Screen Space Shadow的生成,最后才开始正常的渲染流程;(在延迟渲染中,_CameraDepthTexture可以免费获取~);

注意:只有含有shadowcaster pass的物体才可以渲染进_CameraDepthTexture,并且只能是不透明物体( render queue <= 2500);参考Unity官方文档

  1. 在移动平台下,需要显示设置Camera的depthTextureMode,即:came.depthTextureMode = DepthTextureMode.Depth;;此时,可以直接使用_CameraDepthTexture,并且MSAA与HDR一起开好像也没问题;查看frame debugger,会发现先渲染_CameraDepthTexture深度图,然后在渲染一张ShadowMap,然后才开始正常的渲染流程;
  2. 问题来了!,Unity官方说开启HDR就不支持MSAA了,High Light System插件的文档也说开启HDR不支持MSAA,并且开启MSAA会导致无法获取_CameraDepthTexture,参考文档High Dynamic Range Rendering。这一部分问题涉及到硬件,但是我按照前面的1、2使用,没有出现这种问题。如果有大佬清楚还请指导一下==。
  3. 所以说解决方案应该是,关闭MSAA,使用后处理的AA,这样就可以开启HDR并使用_CameraDepthTexture了;

内部便利函数及宏

获取法线自纹理:
UnpackNormal(tex2D(_BumpMap, i.uv));
half3 UnpackScaleNormal(half4 packednormal, half bumpScale);
获取world tangent、binormal、normal向量:
float3 normalWorld = UnityObjectToWorldNormal(v.normal);
float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
 
half3x3 tangentToWorld = CreateTangentToWorldPerVertex(half3 worldNormal, half3 worldTangent, half tangentSign)
{
    // For odd-negative scale transforms we need to flip the sign
    half sign = tangentSign * unity_WorldTransformParams.w;
    half3 binormal = cross(normal, tangent) * sign;
    return half3x3(tangent, binormal, normal);
}
o.tangent = tangentToWorld[0];
o.binormal = tangentToWorld[1];
o.normal = tangentToWorld[2];
linear与gamma空间转换:
inline bool IsGammaSpace();
inline half3 GammaToLinearSpace (half3 sRGB);
inline half3 LinearToGammaSpace (half3 linRGB);
unity内部阴影的使用

阴影的投射只要有shadow caster通道即可;
阴影接受的计算:
方法一:

#pragma multi_compile_fwdbase
 #include “AutoLight.cginc”
 SHADOW_COORDS(1) // put shadows data into TEXCOORD1
 TRANSFER_SHADOW(o)
 fixed shadow = SHADOW_ATTENUATION(i);

方法二:

 #pragma multi_compile_fwdbase
// after CGPROGRAM;
#include "AutoLight.cginc"
 
// in v2f struct;
LIGHTING_COORDS(0,1) // replace 0 and 1 with the next available TEXCOORDs in your shader, don't put a semicolon at the end of this line.
 
// in vert shader;
TRANSFER_VERTEX_TO_FRAGMENT(o); // Calculates shadow and light attenuation and passes it to the frag shader.
 
//in frag shader;
float atten = LIGHT_ATTENUATION(i); // This is a float for your shadow/attenuation value, multiply your lighting value by this to get shadows. Replace i with whatever you've defined your input struct to be called (e.g. frag(v2f [b]i[/b]) : COLOR { ... ).
 

方法三:

#pragma multi_compile_fwdbase
#include "AutoLight.cginc"
UNITY_LIGHTING_COORDS(6,7)
UNITY_TRANSFER_LIGHTING(o, v.uv1);
UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld);

Shader编写坑点

正常normal与detail normal之间的处理:
  1. 方法一:首先进行normal与detail normal的解压,然后由mask在两者之间进行lerp操作获取final normal,最后对final normal进行normalize操作;

注意:其计算空间为tangent space;

  1. 方法二:detai normal是在正常normal的基础上叠加发现,因此就需要根据正常normal与detai normal获取叠加后的temp normal,然后由mask在正常normal与temp normal之间进行lerp操作获取final normal,最后对final normal进行normalize操作;
    获取temp normal的方法为:
    temp normal = normalize(normal.xy + detail.xy, normal.z*detail.z);
    实际中可使用的方法有好几种,各游戏及引擎使用的方法也不一致。可参考文章 Blending in Detail,该文章进行了详细的归纳,并且提供了各种方法的优缺点及其代码!点赞!
Unity shader计算所使用MVP归一化后发现z的范围为[1,0]

按照unity的顶点变换流程,mul( UNITY_MATRIX_MVP, v.vertex)的结果进行归一化后应位于NDC空间,即xyz的范围都为[-1, 1]。
但是在shader手动进行归一化后发现z的范围为[1,0];感觉很奇怪,而如果采用脚本中获取相机的vp矩阵计算,归一化后其深度范围刚好为[-1,1];
事实证明shader中的VP矩阵与相机中的VP矩阵根本不一致;
具体可参考这里;
原文为:
Note: On DX11/12, PS4, XboxOne and Metal, the Z buffer range is 1–0 and UNITY_REVERSED_Z is defined. On other platforms, the range is 0–1.
原文中的宏被废弃了,文档也不更新,呵呵呵。。。

另外使用unity内部的_CameraDepthTexture时,其范围是(0,1);
获取shader中所使用VP:
由于DX平台与OpenGL平台的差异性,unity使用了GL类来进行两者的统一,同样使用该库来进行统一的渲染,因此我们可以使用该库来获取shader中VP矩阵:

Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(mCamera.projectionMatrix, true);
unity_MatrixVP = projectionMatrix * mCamera.worldToCameraMatrix;

参考这里

总结

没事多看看unity内部源码,想用什么从里面摘什么==

你可能感兴趣的:(计算图形学,Unity,Note)