抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

我看了看手上的牌,明白自己无牌可出

主要记录了学习Shader时,由于版本更新,书中与实际操作中不一样的地方,我的unity版本是2021.3.5,因为是中途开始写的,如有遗漏请见谅

官方勘误列表

都快要把书看完了才发现《Unity Shader入门精要》勘误

附带:《Unity Shader入门精要》随书彩色插图

我的代码仓库

yzs020220/my-unity-shaders-book

P35 3.4.2最聪明的孩子:顶点/片元着色器

之后也会用到的,在顶点着色器中将坐标从模型空间转换到世界空间中。代码需include “UnityCG.cginc”(在其他库中经常会被include,比如Lighting.cginc)

#include "UnityCG.cginc"
···
return UnityWorldToClipPos(v);
return mul(UNITY_MATRIX_MVP, v);

P101 5.2.1 去掉天空盒

Window->Rendering->Lighting->Environment->skybox

P113 5.5.3最新利器:帧调节器

Window->Analysis->Frame Debugger

P128 6.4.1逐顶点漫反射光照

在顶点着色器中将法线从模型空间变换到世界空间中

fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));

P134 6.5.1逐顶点高光反射光照

在顶点着色器中将顶点位置从模型空间变换到世界空间中

fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(Object2World, v.vertex).xyz);

P180 9.1渲染路径

Unity的Edit->Project Settings->Graphics中能看到不同图像质量下对应的Rendering Path

P192 9.2.2处理不同光源的衰减

在Additional Pass的片元着色器中计算不同光源的衰减前,要将片元的坐标转换到光源空间,在采样衰减纹理来得到衰减值

float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
float3 lightCoord = mul(_LightMatrix0, float4(i.worldPos, 1)).xyz;

P226 10.3.1声明程序纹理参数

书中使用了一个开源插件,我个人没有去尝试这个开源插件,可能这个开源插件能够让修改更丝滑一点,考虑到其实我们就是检测inspector中是否被修改,然后调用_UpdateMaterial方法就好,要实现这个我们可以使用onValidate方法,这个方法在inspector中参数被修改时会被调用,这个写法也能让代码简洁很多:

[SerializeField] private int textureWidth = 512;
[SerializeField] private Color backgroundColor = Color.white;
···
private void OnValidate()
{
    _UpdateMaterial();
}
[SerializeField, SetProperty("textureWidth")]
private int m_textureWidth = 512;-
public int textureWidth {
    get {
        return m_textureWidth;
    }
    set {
        m_textureWidth = value;
        _UpdateMaterial();
    }
}

P245 12.1PostEffectsBase.cs

检查各种资源和条件的代码,在新版本中已经被弃用,返回值总会是True,所以可以不写

using UnityEngine;

[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectBase : MonoBehaviour
{
    protected Material CheckShaderAndCreateMaterial(Shader shader, Material material)
    {
        if (shader == null) return null;
        if (shader.isSupported && material && material.shader == shader) return material;
        if (!shader.isSupported) return null;
        else
        {
            material = new Material(shader);
            material.hideFlags = HideFlags.DontSave;
            if (material) return material;
            else return null;
        }
    }
}

P264 12.6运动模糊

RenderTexture.MarkRestoreExpected会显示已被弃用,具体可以查看官方文档Unity - Scripting API: RenderTexture.MarkRestoreExpected,大概意思是在移动端,Unity会对还原renderTexture的操作进行警告,因为恢复发生在渲染纹理的时候,不将内容清除或丢弃在许多移动端GPU和多GPU系统中需要很大消耗,不过如果你确实需要(就像我们现在写运动模糊),我们仍可以使用该方法禁用警告(感觉好像没完全被禁用,不过是否使用该代码都不会影响运行结果)。

// We are accumulating motion over frames without clear/discard
// by design, so silence any performance warnings from Unity
accumulationTexture.MarkRestoreExpected();

P275 13.2再谈运动模糊中的片元着色器

如果直接使用书本的代码会有一种很奇怪的感觉,会感觉只是有一大堆重影,总而言之会觉得不对,这是因为物体的运动是有方向的,而书中的代码直接将前两帧和当前帧取平均会让人不知道往哪个方向运动,我在“再谈运动模糊”效果改进建议 · Issue #195 中找到了改进方法,我们可以设置权重,当前帧的权重调大,后两帧权重调小,这样我们就能从三帧混合的图像中知道画面中知道哪一帧在前,我们只需要修改最后对三帧取平均的代码即可

float2 velocity = (currentPos.xy - previousPos.xy)/2.0f;
            
float2 uv = i.uv;
float vecColRate[3] = { 0.7,0.2,0.1 };
float4 c = tex2D(_MainTex, uv) * vecColRate[0];
uv += velocity * _BlurSize;
for (int it = 1; it < 3; it++, uv += velocity * _BlurSize) {
    float4 currentColor = tex2D(_MainTex, uv);
    c += currentColor * vecColRate[it];
}
            
return fixed4(c.rgb, 1.0);
float2 velocity = (currentPos.xy - previousPos.xy)/2.0f;
            
float2 uv = i.uv;
float4 c = tex2D(_MainTex, uv);
uv += velocity * _BlurSize;
for (int it = 1; it < 3; it++, uv += velocity * _BlurSize) {
    float4 currentColor = tex2D(_MainTex, uv);
    c += currentColor;
}
c /= 3;

return fixed4(c.rgb, 1.0);

P302 15.2水波效果

难绷,这个地方不能算是一个错误,但还是提上一嘴,看彩图的图片可能会自然地以为地板是经过水的折射形成这个颜色,但其实地板和墙的贴图颜色是不一样的,地板对应的贴图是Wall04那一张贴图(其实观察墙经过水面和地板经过水面的颜色估计也能察觉到了,但是看着图片太想当然了)。

P313 16.3.2性能分析器

点击Window->Analysis->Profiler打开

P315 16.4.1动态批处理

如果物体满足条件却没有进行动态批处理,很大可能是没有开启动态批处理,在Edit->Project Settings->PlayerOther Settings中找到Dynamic Batching并勾选即可。

具体的没有进行动态批处理的原因可以在Frame Debugger中查看到

P336 17.5 编译指令指定参数不要换行

由于书本的排版原因,会让人误以为这个地方要换行,这一句指定参数的编译指令如果换行会报错(在作者github中的代码是正确的)

#pragma surface surf CustomLambert vertex:myvert finalColor:mycolor addshadow exclude_path:deferred exclude_path:prepass nometa
#pragma surface surf CustomLambert vertex:myvert finalColor:mycolor addshadow 
exclude_path:deferred exclude_path:prepass nometa

P348 18.2 Unity5的Standard Shader(Standard.Shader)

书上说Standard的Shader源代码可以在DefaultResourcesExtra文件夹下找到,但是在新版本中,虽然可以在安装路径下的Editor->Data->CGIncludes中查看到相关头文件,但是却找不到DefaultResourcesExtra这个文件夹,根据在网上论坛上翻的一些讨论,这个文件夹应该是被打包了,我们可以在Editor->Data->Resources中找到一个叫unity-builtin-extra的文件,这个文件是打不开的。如果真的要查看Standard.Shader的源代码,我在github上找到一个项目是比较全的:unity-built-in-shaders,各个版本也可以在branches中查看。

P352 18.3一个更加复杂的例子

需要将导入的.HDR文件的Texture Shape设置为Cube,在Lighting->Scene中需先点击New Lighting Settings然后启用Realtime Global Illumination,建议先拖拽贴图,然后再拖拽项目内的材质,如果是自己新建材质并拖拽贴图(Shader选择Specular Setup),贴图与材质属性大致对应如下:

材质属性 贴图后缀
Albedo a
Specular sg
Normal Map n
Occlusion o
Detail Mask m

还有一些木头材质(如桶、棚)需要设置Secondary Map,还是建议拖拽贴图后拖拽材质,有不少参数需要调整。

场景中物体没有阴影

如果发现物体没有阴影,很可能不是Shader的问题,而是需要设置光源的Shadow Type(默认的平行光会产生阴影,自己手动创建的光源默认Shadow Type为No Shadows,删除默认光源后创建的光源需重新设置)

评论