shader思路以及解决方案汇总:
手游性能指标
    三角形数上限 的建议是 200K
---------------------------------------

#0.调试shader
    Nsight调试
    Adreno Profiler
---------------------------------------

#1. 下雨:雨丝,潮湿模拟,涟漪,水花,积水,雷电,水交互
    1.1雨丝实现方法:
    1.1.1.粒子
    优点:容易表现方向和深度。缺点:消耗大,很难表现密集
    1.1.2.在屏幕空间做一个纹理动画(后处理,首选)
    优点:密集与否性能消耗都一样
    难点:雨的方向与视角变换;深度视差;场景遮挡,湿滑的衣服质感
    方向:控制椎体的倾斜
    场景遮挡:在3D空间中创建一个椎体包围相机,在椎体上做纹理动画,通过调整椎体的倾斜度表现雨的方向,还可以通过增大椎体倾斜和拉长、加快纹理动画表现出镜头移动加速的效果。
    椎体的上下两个顶点解决了向上向下看的问题
    远近层次,不规则性模拟:四层纹理,表现远近,越远的层纹理tilling值越大,运动速度越慢,而且每层的动画方向稍有不同,表现出雨滴运动的不规则、交错感
    潮湿模拟:调整材质的Gloss、Specular,加深diffuse
---------------------------------------

#2.次表面散射
    常用于皮肤,牛奶,奶油奶酪,番茄酱,土豆等
    基于光源深度图实现sss图
    简单的用深度映射模拟吸收的方式。该方法的基本思想是:光在材质中的传播的越远,它的散射和吸收的越厉害。为模拟此效果我们需要计算光在材质中传播距离。计算传播距离的方式则是通过深度图,具体步骤如下:
    1. 以光源为视点渲染整个场景,存储光源到物体对象的最近距离。
    2. 在相机空间绘制对象时,先将顶点数据转化到光源视点空间,由光源深度图读出深度信息与该顶点在光源空间距离灯光的距离(深度)做差,求出光在物体对象中的传播距离如下图:
    注: 在灯光空间绘制深度图时,记录Object对象距离灯光的最近距离如di1 di2(Object 上半部分)。 在视图空间绘制时do1 do2时,
    先将do1 do2转化到灯光空间(转化到灯光空间后 di2 do2将会有相同的x y值 zi2 zo2为其在灯光空间的深度信息也就是到灯光的距离)用两者在灯光空间深度值做差可以计算出光在该点传播的距离。
    3. 由距离s可以通过美术给的一张一维颜色索引取得颜色值,也可以通过指数e(-s)*lightColor计算最终颜色。

次表面散射的实时近似方法,总结起来有三个要点:
    https://www.jianshu.com/p/3645fc40d0ff
    https://gitee.com/lengfuganjue/Game-Programmer-Study-Notes/blob/master/Content/%E3%80%8AGPU%20Gems%201%E3%80%8B%E5%85%A8%E4%B9%A6%E6%8F%90%E7%82%BC%E6%80%BB%E7%BB%93/README.md?_from=gitee_search#42-%E7%AE%80%E5%8D%95%E7%9A%84%E6%95%A3%E5%B0%84%E8%BF%91%E4%BC%BC%EF%BC%88simple-scattering-approximations%EF%BC%89
    1) 基于环绕照明(Warp Lighting)的简单散射近似,Oren-Nayar光照模型。
        Lambert漫反射提供的照明度是0。而环绕光照修改漫反射函数,使得光照环绕在物体的周围,越过那些正常时会变黑变暗的点。这减少了漫反射光照明的对比度,从而减少了环境光和所要求的填充光的量。环绕光照是对Oren-Nayar光照模型的一个粗糙的近似。原模型力图更精确地模拟粗糙的不光滑表面
        wrap变量为环绕值,是一个范围为0到1之间的浮点数,用于控制光照环绕物体周围距离
    2) 使用深度贴图来模拟半透明材质的最重要特性之一——吸收(Absorption)。
        在第一个通道(first pass)中,我们从光源的视点处渲染场景,存储从光源到某个纹理的距离。然后使用标准的投射纹理贴图(standard projective texture mapping),将该图像投射回场景。在渲染通道(rendering pass)中,给定一个需要着色的点,我们可以查询这个纹理,来获得从光线进入表面的点(d_i)到光源间距离,通过从光线到光线离开表面的距离(d_o)里减去这个值,我们便可以获得光线转过物体内部距离长度的一个估计值(S)
    3)基于纹理空间中的漫反射模拟(Texture-Space Diffusion),来模拟次表面散射最明显的视觉特征之一——模糊的光照效果
        可以用顶点程序展开物体的网格,程序使用纹理坐标UV作为顶点的屏幕位置。程序简单地把[0,1]范围的纹理坐标重映射为[-1,1]的规范化的坐标。
        另外,为了模拟吸收和散射与波长的相关的事实,可以对每个彩色通道分为地改变滤波权重

关键词;
        皮肤渲染(Skin Rendering)
        次表面散射(Subsurface Scattering)
        纹理空间漫反射(Texture-Space Diffusion)
        环绕照明(Warp Lighting)
        深度映射(Depth Maps)
---------------------------------------

#3.皮肤,两种做法
    1.预积分皮肤,也叫曲率皮肤,效率较高,游戏里选这个
        https://blog.csdn.net/toughbro/article/details/7391935   <- 曲率图的计算
        https://zhuanlan.zhihu.com/p/43244741
        https://zhuanlan.zhihu.com/p/70390192
        https://zhuanlan.zhihu.com/p/43239846
        公式理解:使用用积分,根据profile计算球面上每一点入射的光线,经过球体的折射之后从某一点出射的光通量。这个光通量基本上是跟球体的半径成反比的。
        曲线的曲率定义(curvature)就是针对曲线上某个点的切线方向角对弧长的转动率,通过微分来定义,表明曲线偏离直线的程度。数学上表明曲线在某一点的弯曲程度的数值。
        LUT图:所有的入射光线,通过散射之后出射的光通量跟曲率近似成正比关系。通过一系列的复杂计算和经验积累,加上法线和光线方向的影响,得出左黑右白的曲率图,可参看以上链接
        从美术角度理解这张LUT图:图用来模拟明暗交界线处颜色会要更加饱和这一现象,加上距离越远出射光越少
        曲率图需要模糊:曲率图如果不进行模糊处理的话,会呈现块状的效果
        
        比较取巧的方法:Matcap图辅助控制光照过渡区域的高饱和效果
        
    2.Screen-Space Subsurface Scattering,这种方法效率较差
        http://blog.csdn.net/wolf96/article/details/49678163
        是依赖次表面散射,而散射体现出来的效果包括反射,漫射和透射。Unity内置的BRDF1公式,可以基本满足我们对于皮肤表层正常反射的要求,我们通过一个受一张深度渲染控制的高斯模糊后处理来实现漫射效果,
        从而模拟出皮肤的柔软质感,即Screen-Space Subsurface Scattering, 然后再通过一张Cull Front Shader渲染出的深度图,进行对物体厚度的模拟,进而模拟出光线透射的效果,最后再加上Unity官方的Postprocessing

---------------------------------------

#4.光照模型比较
Lambert 光照模型(环境光+漫反射)
    Idiff = kd * Ia + kd * Il * (N·L) = kd * Ia + kd * Il * dot(N, L)
        Ia 是环境光的强度
        kd 为材质对环境光的反射系数(0 < kd < 1)
        Il 是方向光的强度
        kd 为材质对环境光的反射系数(0 < kd < 1)
        θ 是入射光方向和顶点法线的夹角。当夹角为 0°,说明入射光平行于法线(垂直于表面),此时反射强度最大;当夹角为 90° 时,说明入射光同表面顶点切线平行,此时物体不会反射任何光线。
        N 顶点单位法向量 
        L 与从顶点指向光源的单位向量
    Unity 中的 Lambert 光照模型的源码:
    inline fixed4 UnityLambertLight (SurfaceOutput s, UnityLight light)
    {
        fixed diff = max (0, dot (s.Normal, light.dir));
        fixed4 c;
        c.rgb = s.Albedo * light.color * diff;
        c.a = s.Alpha;
        return c;
    }

Half Lambert 光照模型(环境光+漫反射+提亮暗部)
    Half Lambert 用来给在比较暗的区域显示物体
    inline half4 LightingCustomLambert(SurfaceOutput s, half3 lightDir, half atten)
    {
        // Lambert
        half diffLight = dot(s.Normal, lightDir);
        // half Lambert
        diffLight = diffLight * 0.5 + 0.5;
        half4 c;
        c.rgb = s.Albedo * _LightColor0.rgb * (diffLight * atten * 1);
        c.a = s.Alpha;
        return c;
    }
    在 Lambert 的基础上,通过 diffLight = diffLight * 0.5 + 0.5,使得 diffLight 变大了从而增强了在光线暗的区域的视觉效果。

Phong 光照模型(环境光+漫反射+高光反射)
    Phong在Lambert模型的基础上,加入对光滑表面的高光反射
    Ispec = ks * Il * (R·V) ^ p
    R + L = 2 * N * (N·L) = > R = 2 * N * (N·L) - L
    得到:Ispec = ks * Il * ((2 * N * (N·L) - L)·V) ^ p
    ks 是材质的镜面反射系数
    Il 是光强
    R 为反射光的方向
    V 表示从顶点到视点的方向
    p 是高光指数,p 越大反射越集中,当慢慢视线方向偏离反射方向光线开始慢慢衰减,反之 p 越小观察到的光斑区域也就越小,反射光强度也很弱。
    N 单位法向量 
    L 从顶点指向光源的单位向量

Blin-Phong光照模型(环境光+漫反射+高光反射,表现和Phong 光照模型差不多,但优化了效率)
    Ispec = ks * Il * (N·H) ^ p
    ks 是材质的镜面反射系数
    Il 是光强
    N 为入射点的单位法向量
    H 表示光线方向和视角方向的半角向量
    p 是高光指数,p 越大反射越集中,当慢慢视线方向偏离反射方向光线开始慢慢衰减,反之 p 越小观察到的光斑区域也就越小,反射光强度也很弱    
    用到了视角方向和光线方向构成的半角向量
    Unity 中的 BlinnPhong 光照模型的代码:
    inline fixed4 UnityBlinnPhongLight (SurfaceOutput s, half3 viewDir, UnityLight light)
    {
        half3 h = normalize (light.dir + viewDir);
        fixed diff = max (0, dot (s.Normal, light.dir));
        float nh = max (0, dot (s.Normal, h));
        float spec = pow (nh, s.Specular*128.0) * s.Gloss;
        fixed4 c;
        c.rgb = s.Albedo * light.color * diff + light.color * _SpecColor.rgb * spec;
        c.a = s.Alpha;
        return c;
    }
    首先计算了光线方向和视角方向的半角向量 h,接着计算了 Lambert 光照模型中计算光强的乘法因子 diff,然后又计算了法向量和 h 的点积 nh,
    最后通过指数计算得到了高光乘法因子 spec,最终输出就是 Lambert 光照模型得到的漫反射值以及 BlinnPhong 光照模型得到的高光反射值的和

BRDF
    基于物理的光照模型
    一次反射光照的计算是在光线交点的法线半球上的球面积分
    双向反射分布函数(Bidirectional Reflectance Distribution Function,BRDF)用来定义给定入射方向上的辐射照度(Irradiance)如何影响给定出射方向上的辐射率(Radiance)。
    宏观来看,它描述了入射光线经过某个表面反射后如何在各个出射方向上分布——可以是从理想镜面反射到漫反射、各向同性(Isotropic)或者各向异性(Anisotropic)的各种反射。
    计算机图形学中实现BRDF理论模型的一个方法是用微小面元对物体表面进行建模,每一个小平面都是表面上的一个小平面镜,具有随机的大小和角度。这些小平面通常被赋予一个高斯分布的尺寸和角度。

BSSRDF
    基于物理的光照模型
    用于模拟皮肤,BSSRDF可以指定不同的光线入射位置和出射的位置
    每一次反射在物体表面上每一个位置都要做一次半球面积分,是一个嵌套积分
    最初来源于Jensen在2001年的论文是次表面材质建模最重要的一篇论文[Jensen, Henrik Wann, Stephen R. Marschner, Marc Levoy, and Pat Hanrahan. 2001. "A Practical Model for Subsurface Light Transport." In Proceedings of SIGGRAPH 2001.],
    推导了许多重要的物理公式,计算模型,渲染时的参数转换,以及测量了许多生活中常见材质的散射系数等

#5.《镇魔曲》手游水、皮肤材质渲染的思路
    https://www.zhihu.com/question/265769844
    第一个思路:
    皮肤用Pre-Integrated Skin 或者 pbr + translucent 简单模拟都是可以的。下面是translucent函数。// Translucency.
    half3 transLightDir = lightDir + s.Normal * _Distortion;
    float transDot = pow(max(0, dot(viewDir, -transLightDir)), _Power ) * _Scale;
    fixed3 transLight = atten * transDot * s.Alpha * _SubColor.rgb;
    fixed3 transAlbedo = s.Albedo * _LightColor0.rgb * transLight;
    只是指个方向和关键之处,计算的结果最后需要用一张mask图控制一下。从贴图上大致看是否一致。但感觉题主更像Pre-Integrated那种,或者两者混合。对于unity5x 不再是 (atten * 2)了。
    水的话运行可以简单一个法线uv动画叠加搞定,至于深度,可以在摄像机上开启Depth_camera.depthTextureMode |= DepthTextureMode.Depth;这样在shader中可以访问屏幕depth, 这里简单按深度差线性计算了一个alpha值UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);

//vert shader:
    o.projPos = ComputeScreenPos(o.pos);
    o.projPos.z = o.pos.w;

//fragment shader:
    float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));                        
    opacity = 0.3 + saturate((sceneZ - i.projPos.z) / 5); 
    ....
    return half4(color, opacity);

第二个思路:
    水实现的比较省,就是uv动画。只是实现的比较巧:
    1.在水的网格上存了顶点色和相对深度;
    2.输入一个深度颜色、一个阴影颜色跟相对深度计算出一个颜色,再用这个颜色和顶点色作用得刀diffuse; 
    3.最后diffuse 和贴图做个插值算出最终值
    
    第三个思路:
    水体没有做折射的,这种半透明效果通过深度或者高度控制一下Alpha

#能量罩
    相加:1+相机视野深度[Near,Fra]-当前深度
    边缘:float intersect = (1 - (screenZ - i.eyeZ)) * _IntersectionPower;
    叠加相交和边缘得到一个变量值用来控制颜色显示:float v = max (rim, intersect);

#头发
2层高光,2个pass,第二层暗淡一些,通过法线偏移控制高光位置变化,通过噪点图
第一个pass用AlphaClip解决头发叠加穿透问题:clip(albedo.a -_Cutoff);
第二个pass用Transparent,解决头发硬边问题

#遮挡剔除
2个pass,被遮挡的用以下pass
Pass
{
            Blend SrcAlpha One
            ZWrite off
            //深度大于深度缓冲时才绘制,即被不透明物体挡住时才绘制
            ZTest greater
}
---------------------------------------

#8.GPU绘制草海
https://www.jianshu.com/p/dd160efbc4bb
插件:uNature , Advanced Terrain Grass, Nature Renderer ,Lux LWRP Essentials 
uNature的网格合并
    是对草先做一定程度的 Mesh合并,可以通过控制格子的粒度,从而把每个 紫色格子 内的 蓝色格子 数控制在 1023 以内,然后就可以通过 Graphics.DrawMeshInstanced 接口一次完成渲染    
uNature的缺点
    代码有不少bug,很久没更新代码了
    针对移动端还要做很多优化
    如果格子数很多,uNature 还有明显的 GC负担,需要自己优化
    GetDensity函数的浮点数比较有问题,会导致有些机型草不显示,要改为 if(abs(pixel.r - _PrototypeID) < 0.01f) return pixel.g;
Advanced Terrain Grass的缺点
    以每一株草为单位来渲染
    是基于 Graphics.DrawMeshInstancedIndirect 的实现。此外,如果你想实现类似 塞尔达的割草 效果,整个 ComputeBuffer 的数据都要重建,这个开销在运行时难以承受
Nature 缺点
    不开源

这几个插件大致做了以下优化:
    利用 GPU Instancing 提速渲染( 把Mesh的计算从CPU移到GPU:把影响 Mesh差异 的因素 ( 比如 Noise 和 高度 ) 编码到纹理,然后在 顶点着色器 采样纹理再把这些差异应用到顶点,编码到纹理的源码在uNature中能找到)
    消除 Terrain Patch 运行时 合并Mesh 造成的 CPU峰值。
    利用 多线程设计 分担 主线程 的负担。
    提升 摆动表现,提升 光照表现。

需要注意的:
    美术
    必须尽可能的压低模型草的顶点数
    面片3个顶点的三角形,要么是 4个顶点的菱形
    LOD 对降顶点数非常重要,可以做3级LOD
    程序
    选择合适的 Fade Distance。
    选择合适的 LOD 策略。
    选择合适的 Patch Size,
        视锥体剔除,剔除的精度和 Patch Size 相关,Patch Size 越小,剔除越精准
        DrawMeshInstanced 有最大数量 1023 的限制,这个限制和当前设备的 Uniform Buffer Object 最大容量有关,总格子数越多,遍历的开销就越高,需要的内存也越多
        总格子数越多,遍历的开销就越高,需要的内存也越多
    尽可能的剔除。
        开自阴影的效果更好,代价是草海会被 绘制两遍,第一遍写 ShadowMap,第二遍才是 场景绘制,会导致DrawCall翻倍,代价比较大根据项目决定是否开启
        设定阀值,距离视点近的草支持随风动,远处是不动的草体贴片
        草体颜色和Terrain颜色需要比较接近,这样草的LOD切换会自然
        需要草的diffuse材质/pbr材质,要能接收阴影,不需要alpha cut
        沿着地形生成一个多随机顶点的mesh(需要HeightMap)
        geometry shader的做法
            做一个geometry shader,在需要产生草的顶点上生成单根的草
            哪里产生草,哪里不产生草,可以在v2g的参数上里传入,这个值可以在vert函数里采样一张mask图
            随风摇摆,可以通过perlin noise函数来移动地面以上的几个顶点
            人物和小怪踩倒的交互。目前的方法是把人物的世界坐标传入geometry shader,然后根据草的顶点和人物坐标的距离来计算压倒的强度和方向,这样做的问题是,如果有很多小怪和宝箱,就要实时传入一个坐标数组,性能会比较差,另外也解决不了延迟抬起的问题

首先做一个草的diffuse材质,要能接收阴影,不需要alpha cut,因为荒野之息里面的单根草就是两个三角形的一个quad,上下两个尖,中间一条线,然后要加上instancing;
沿着地形生成一个多随机顶点的mesh;
然后做一个geometry shader,在需要产生草的顶点上生成单根的草;
哪里产生草,哪里不产生草,可以在v2g的参数上里传入,这个值可以在vert函数里采样一张mask图;
然后随风摇摆,可以通过perlin noise函数来移动地面以上的几个顶点;
关键的是人物和小怪踩倒的交互这里我没有想出什么好方法。目前的方法是把人物的世界坐标传入geometry shader,然后根据草的顶点和人物坐标的距离来计算压倒的强度和方向,这样做的问题是,如果有很多小怪和宝箱,就要实时传入一个坐标数组,性能会比较差,另外也解决不了延迟抬起的问题;
还有一个思路是用正交相机,拍摄一个render texture,可以在compute shader里做纹理淡出,类似雪地轨迹那种做法,这样可以支持多物体同时计算,延迟抬起,不过这个问题是这个正交相机怎么才能实现开放场景大地图的同步?还是把整个场景地面分块,用多个正交相机同时工作,因为远处可以不用工作,所以极限情况是地块的交界处,有四个正交相机在同时工作,当然也可以通过优化,不让这些交界处有连续的草地,可以节约很多cpu性能。
这个草地分块的思路来自于flower的技术分享,整个场景切分为256个地块,在ps3的spu上产生草和物理数据。

http://walkingfat.com/dynamic-grass-%e5%8a%a8%e6%80%81%e8%8d%89%e5%9c%b0%e5%88%b6%e4%bd%9c/
http://walkingfat.com/%e8%bf%98%e5%8e%9f%e3%80%8a%e5%a1%9e%e5%b0%94%e8%be%be%e3%80%8b%e7%9a%84%e8%8d%89%e5%9c%b0/
https://zhuanlan.zhihu.com/p/52882293
https://blog.uwa4d.com/archives/TechSharing_200.html
https://edu.uwa4d.com/course-intro/0/78    (提到了几个优化,没有代码)
https://www.cnblogs.com/crazii/p/7337143.html    (这篇的效果超赞)
随风摆动,碰撞弯曲,可破坏,可点燃
草用模型草还是贴图草
    面片草顶点数少,但是依赖 AlphaTest,这一步在很多移动设备上非常昂贵
        单个草由三个方向的草贴图组成(缺点:垂直方向上会露馅,无法用GPUInstancing,交互效果不好做,所以Pass)
    模型草顶点数高,但因为是 实体渲染,性能表现远远优于 AlphaTest    
        单个草三角形或者2个三角形组成的菱形模型,实例化一堆草作为一个Chunk,二维数组加一点偏移,随机设置草方向,大小,然后通过Mesh.CombineMeshes合并成一个Mesh
    最终选模型草,除了性能优势之外,还有美术表现的优势,比如更好的光照表现,更好的碰撞表现,顶视图下的 模型草 也不会穿帮    
每跟草自身的随机摆动(风吹时草根不动,越往上的顶点摆动越大)
    一张上下渐变贴图,用来控制摆动摆动偏移率
    随机摆动核心是每根草的xz坐标作为随机因子导入cos和sin函数
    可以用泰勒展开式的技巧,让草有此起彼伏的效果
    也要有y轴偏移,能体现出草以根部为圆心进行摇摆的效果,y偏移根据xz算出来
草的lod(贴图和模型,3层草)
    1是模型和贴图混合
        远处贴图草,很稀疏(公告板)
        中间草是3片为一组的贴图草
        近处草是模型草,并且很密集
        用unity的load做或者自己控制(?可能是cs代码里用视锥距离去控制)
    2是全模型,带lod。单个模型多片草组成,lod0大约100顶点,lod1大约50顶点,lod2大约25顶点
移动时细节表现
压弯草
    传入障碍物坐标,通过Mesh.SetGlobalVector和SetGlobalVectorArray输入,且在shader里做循环处理
    传入压的力度
    压弯的方向,通过障碍物和草坐标的距离差值,计算得到草顶点的偏移量,并且越往下偏移越小(偏移量乘以y轴梯度控制[共用草自身的随机摆动里的贴图])
    需要保证弯曲时草的长度是不变的,2种做法
        1是以草根部的pivot为圆心旋转(?感觉需要在shader里算弯曲的角度)
        2是先做xz的顶点偏移,在根据三角形边长公式。再算y轴偏移,或者加个重力的参数,也是上方受重力影响更大
        需要对摇摆幅度做限制,防止插进地里
草的过度效果
    近处的草,顶点向下偏移一点,模拟Fadeout
    近处草做xz轴缩放做fadeout,让草越来越窄,逐渐消失
    中间的草和远处的草通过调整AlphaClip(距离越远,clip越强)    ,让每根草看起来越来越细,逐渐消失掉(这个只在贴图草做,且AlphaClip在手机上很费,在shader上应该是通过距离视点的远近来判断是中间草还是远处草)
风吹时向一个方向倒,风的强度
    根据传入的风方向,换算成normalized的Vector2的值,根据风速偏移x,y,z轴
风吹时麦浪的效果(参考:战神RT风场,塞尔达),多种做法
    1是一张有黑白浪的Mask图,直接做uv滚动,进行采样,采样值作为偏移加到偏移算法里
    2是用Noise图,加上sin或cose做uv滚动,进行采样,采样值作为偏移加到偏移算法里
地形/草的高度(HeightMap),草的密度和是否渲染(DensityMap)
    通过采用HeightMap和DensityMap图得到
草叶压弯后延迟抬起,几种做法(?没想明白)
    https://www.zhihu.com/question/271474165
    一个正交相机+rt+粒子
    多个正交摄像机,只要渲染单独的一层
    正交相机,拍摄一个render texture,可以在compute shader里做纹理淡出,类似雪地轨迹那种做法,这样可以支持多物体同时计算,延迟抬起,
    不过这个问题是这个正交相机怎么才能实现开放场景大地图的同步?还是把整个场景地面分块,用多个正交相机同时工作,因为远处可以不用工作,
    所以极限情况是地块的交界处,有四个正交相机在同时工作,当然也可以通过优化,不让这些交界处有连续的草地,可以节约很多cpu性能。
    这个草地分块的思路来自于flower的技术分享,整个场景切分为256个地块,在ps3的spu上产生草和物理数据。
传入的障碍物带有高度,用于跳起时,草的弹起
    传入障碍物的最高坐标和最低坐标
草的光照渲染(pbr,草法线偏移)
    参考:Advanced Terrain Grass ,塞尔达和楚留香的草          
    没有高光的风格化草海:The Illustrated Nature 的做法: 纯色贴图 + Lambert漫反射 + Color Grading
    1是使用实时光
        pbr做高光( BRDF需要简化,省掉菲涅尔)/ 线性空间 Blinn-Phong
        参考:https://www.jianshu.com/p/59e7311a5147     和   Advanced Terrain Grass(ATG 的高光基本沿用了Unity内置的 BRDF1 算法,最高效的BRDF)    
        ATG里有次表面散射(Light Scattering),次表面散射he pow有几个优化,
            参考:
                https://www.slideshare.net/colinbb/colin-barrebrisebois-gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurfacescattering-look-7170855
                https://colinbarrebrisebois.com/2012/04/09/approximating-translucency-revisited-with-simplified-spherical-gaussian/
        草需要整体成片的感觉,需要和Terrain融合在一起,可以把草的法线修改成Terrain的法线(草的法线 全部向上),根Terrain的PBR算法一样,这样草的光照效果和地面光照效果就一样了
    2是用射线取得烘培贴图参考:https://edu.uwa4d.com/lesson-detail/78/120/0?isPreview=0
        这种方式射线检测取LightMap时有性能问题,通过预计算,预加载处理去优化,可参看最后的优化部分
草的Fresnel和SSS效果
    利用草的y轴高度做Mask,让草的上半段逐渐有光线投射和高光反射的效果,这样高低坡落差的地方和麦浪起伏时会有层次感(不做高度差的变化,表现可能是整片草微微荧光的感觉)
    sss,加上后会有阳光从背后照射时晶莹剔透的效果
大量草的绘制,多种方法,但前几种消耗大(接口Graphics.DrawMeshInstanced 和 DrawMeshInstancedIndirect 和 DrawProcedural[需要openGLES3.1])
    1是静态合批,在cs代码里将靠近的草打成一个mesh,这样会产生很多合批后的mesh,在运行时九宫格/四叉树管理(缺点:包体会变大)
    2是初始化草地时进行动态合批(缺点:会有很大的内存占用,出现卡顿)
    3是进行划分管理(选这种),2种方法
        1是
            按Terrain->Chunk->Grid->Grass划分,根据角色位置算出附近4个chunk(需要根据视野宽度范围判断哪4块,也就是旋转时这4块会变化)
            256×256的chunk里有几千个grid,这一步在c#侧处理
            最后进行筛选后,通过GPU instance画出来
            Chunk管理模式的数据组织
                GrassList:存储这个Chunk里的所有草的id,可能需要很长的一个长度固定的数组
                GridList:这个Chunk里的Grid列表,存储每个Grid里草的起始id以及长度(这个Grid里的草数量)
                运行时以Grid为单位进行camera剔除,筛选出视野内的Grid,再得到需要画的草数据
                离线生成Chunk数据
                或者根据HeightMap和DensityMap运行时初始化(这种方法会比较复杂,一般项目里会做工具刷地形,不采用这个控制)
                DrawMeshInstanced一次只能画1023个,所以一个Grid里草数量最多只能为1023,每个Grid做一次DrawMeshInstanced
                这种方式单位模型是单个草模型,Grid只是数据层上的,用DrawMeshInstanced把这一个Grid里的n个草合批在一起
                DrawMeshInstanced和DrawMeshInstancedIndirect的区别
                    DrawMeshInstancedIndirect要用到compute shader,手机很多都不支持的
        2是
            用uNature , Advanced Terrain Grass等插件进行划分管理
            别人的效果是100m范围的草,10-15万个顶点,12-15个drawcall
    4是在3的基础上,改绘制方法
        把一个Grid的草做出一个通用的Mesh,把每根草相对于这个Grid的Pivot偏移量刷在顶点色的RGB上(因为顶点色精度低,只能做2×2米的)
        运行时把这个Grid进行GPUInstance,然后在shader里通过顶点色的offset和mesh的坐标,算出草的坐标
        采样HeightMap算出y轴坐标和法线,采样DenisityMap算出草的Scale和显示与否(黑色代表不显示,白色代表显示,颜色越偏黑色scale越小?没想明白怎么在shader中处理Scale)
        这种方式的单位模型是一个Grid的草,在shader中根据顶点色算出单个草的位置,再做处理(这种方式GPUinstance会少一些,但会多2次采样贴图)
    5是使用以下几个库的树去处理
        https://lab.uwa4d.com/lab/5be3557b72745c25a82dc67c (KDTree库做空间查找)
        https://lab.uwa4d.com/lab/5cbf31c172745c25a83ecb12  (二进制搜索树)
        https://lab.uwa4d.com/lab/5bc5517f04617c5805d4ea0c  (二进制搜索树)
视锥裁减,视距裁减,多种做法
    参考:https://www.jianshu.com/p/001fd20270d7
          https://blog.csdn.net/foxyfred/article/details/74357093 (CullingGroup API的使用说明)
        1是
            将草地按区域分组,用每组的中心点计算视距,依据距离切换网格LOD或剔除;还能用向量点乘简单剔除在相机后方的草地(注意临界问题)。
        2是
            借助CullingGroup。
            CullingGroup.onStateChanged事件绑定,通过事件触发调整传入;DrawMeshInstanced的Matrix顺序和渲染数量(但是DrawMeshInstanced只能指定渲染前几个Matrix);
            通过cullingGroup.SetBoundingSpheres实现视锥体剔除和遮挡剔除;
            通过cullingGroup.SetBoundingDistances实现视距剔除和LOD。
            这个方案最好也进行区域分组,不然CullingGroup的事件监听占用会比较高,在中端机上4000个监听会占约2ms的大小
        3是
            只需要知道草块是否能被渲染,可以用视锥体+射线的方法大致判断出来
割草
    计算出 攻击范围(圆形攻击,扇形攻击,矩形攻击) 覆盖的单元格,并且修改这些单元格的密度
    运行时修改DenisityMap图(是否需要复制出来一份,否则会改到原图)
    有的项目用unityTerrain,那么割草时
        Unity内置的 Terrain 提供了一个 GetDetailLayer 接口,这个接口返回一个二维数组,这个二维数组和 栅格化 后的地表网格是相对应的,数组每个元素的值即当前格 草的密度
        uNature 用一张纹理 GrassMap 来存贮密度
        
烧草(需要燃烧度,也就是草发黑的程度),2种做法
    1是比较耗内存的做法是模仿密度图,做一个燃烧图
    2是复用密度图,对密度和燃烧度进行编码,让他 即能表示密度,也能表示燃烧度
        比如 密度 的最大值定为 10,燃烧度 的最大值我们定为 20,最大值为11 * 21 = 231 ,在 255以内,用密度图的一个通道就可以存储
        shader中 存好密度和燃烧度的最大值定值,再反推出这两个值
        割草和烧草都在c#侧更新DenisityMap图
自阴影(代价太高舍弃掉)
    开自阴影的效果更好,代价是我们的草海会被 绘制两遍,第一遍写 ShadowMap,第二遍才是 场景绘制。
AO
    shader要能接收阴影
    烘培的AO 对于 非静态的 的草来说有点不合适,后处理的AO 则过于昂贵,对于手游来说更不合适。
    可以通过 顶点色 来记录 AO强度,或者直接草的贴图增加一个 AO通道
    参考: Lux LWRP Essentials
水面有反射,绘制量会增加接近一倍
    可能会导致卡顿
草地在不同的天气的不同表现,雨天的湿滑
真机上看效果和帧率
顶点色编辑器制作
    https://www.bilibili.com/video/BV1JW411J7zL?p=13
草的优化
    lod
        lod3级别作为BillBoard实现,为了控制填充率,不是Transparent,而是TransparentCutout
        渲染队列选择(也是控制填充率),lod1和lod2的模型草用Opaque渲染,lod3的BillBoard草,用TransparentCutout渲染
    地块预计算
        使用射线取地块的烘培贴图信息对草进行光照处理时,会有性能问题,可以在离线时,查询DenisityMap得出会生成草的地块,将这个信息存储下来,运行时只射线检测这些有草的地块,取灯图信息
    地块预加载
        地块用kdTree或松散四叉树,视野范围内的地块以高优先级加载,距离远的协程预加载隐藏起来,之后进入视野范围了再显示出来
        可以考虑在四叉树中用加载队列和释放队列进行地块加载卸载处理
    填充率
        Graphic.DrawMeshInstanced是根据传入数组顺序渲染的,unity没有做排序,导致很多pixel无意义的被渲染
        对需要渲染的草块进行排序
        对草块里的Grid进行排序,对Grid里的草进行排序,可降低填充率
        排序算法可使用堆排序 或 计数排序,参考:https://edu.uwa4d.com/lesson-detail/78/120/0?isPreview=0

---------------------------------------

8.扫描线
指定深度和当前片段深度越靠近,则v值越接近0,越偏向线颜色
参考:https://www.jianshu.com/p/80a932d1f11e
---------------------------------------

9.能量场
边缘+相交(通过eyeZ和screenZ的差值计算)+透明
参考:https://www.jianshu.com/p/80a932d1f11e
---------------------------------------

10.运动模糊(第二种更好)
第一种是将当前帧和下一帧或上一帧等等图像混合起来作为当前的屏幕图像,这样做法可以导致物体运动时会出现多个残影(因为多个帧混合起来了),可以产生运动模糊效果。缺点:效率不高
第二种做法是,在Shader里面利用View-Projection矩阵及其逆矩阵获得当前帧和上一帧的世界坐标,通过这两个坐标得到当前像素的运动速度及其方向,再根据这个速度,向这个像素速度方向的N个纹素进行取样并混合,从而产生模糊效果。
---------------------------------------

11.景深
第一步是使用Blur Shader渲染模糊的图
第二步就是传递该模糊的图给DepthOfField Shader
第三步就是在DepthOfField Shader中根据深度和焦点位置来混合原图颜色和模糊图颜色
---------------------------------------

12.锐化
对点进行偏导,ddx+ddy
---------------------------------------

13.抗锯齿
fwidth+smoothstep+clamp
---------------------------------------

14.Tessellation (曲面细分)
作用:
低精度模型通过算法转变为高精度模型,也就是增加三角数
---------------------------------------

15.交互草地
风吹:随时间变化采样噪点图,tick:附加噪点颜色和位置偏移后形成风吹时草光照变化引起颜色变化的感觉
越是尖部移动越大:顶点偏移乘上uv.y,越往下uv.y越接近0,偏移越小
交互:传角色位置到shader,草定点减去角色位置得到被压弯的方向,同时进行作用半径判断。tick:草定点和角色位置距离越大,受到压弯的偏移越小。压弯偏移量可以往上调,防止草穿过地面
交互优化:如果是大面积的草地顶点数量很多的时候,可以写两份Shader,一个是带交互的,一个是不带交互的。然后把草地分块再加上Trigger,默认都使用不带交互的Shader,当角色或物体进入到某块草地的Trigger时动态把这块草地的Shader替换成带交互的,这样Trigger以外的顶点都不会参与计算了
---------------------------------------

16.卡通头发
各向异性
梯度漫反射(_Ramp),就是漫反射再和一个一维梯度纹理采样的颜色相混合,用来模拟卡通里条带状的亮部和暗部
---------------------------------------

17.描边
    17.1 细节勾边(描边的一种方法),原理就是那个文章里写的判断一条边相邻两个三角形的法线角度大于某个值就画这条线
    17.2 普通勾边
        方法1.前后面绘制偏移:offset -1,0
        方法2.2个pass,第一个pass绘制背面,法线挤出一些,绘制描边颜色,第二个pass绘制正面

----------------------------------------
18.魔兽地形分析
    世界分为多个MapWorld,每个MapWorld分为64x64个MapTile。每块MapTile又进一步分为16x16个MapChunk
    魔兽采用多层地形纹理混合的方式来渲染地形,这也是各游戏普遍采用的方式。一般情况下,魔兽使用4张地形贴图和一张混合贴图,混合贴图的R、G、B通道表示前3次混合的权重,A通道表示是否为阴影。地形贴图一般在MapChunk单方向上重复张贴8次;而通常尺寸为64x64的混合贴图,仅重复一次,即每一个MapChunk对应一张混合贴图。这张64x64的贴图,在混合地形时,为地形的混合精细度提供了足够的保障。
        尚不清楚出于何种考虑,魔兽中保存了反映当前WorldMap粗略高度的一个64x64的顶点列表,对应64x64个MapTile。渲染之初,使用当前雾的颜色作为输出色,首先渲染这个列表。这样做可能的一个原因,是当游戏视野设置很低时,稍远处的地形会被剪裁掉,而这个顶点列表在雾的配合下,提供远处的地形填充,不至于出现地形截断的效果。
        接着,针对活动的3x3个MapTile逐一进行绘制。在每个MapTile中,使用四叉树管理其下的MapChunk,每个MapChunk根据相机和自身的实际距离,选择采用如下渲染策略:
    1、距离很远。采用二级顶点索引,使用NoDetail渲染策略。仅设置雾颜色为顶点色,不考虑纹理采样和光照处理。
    2、距离适中。采用二级顶点索引,使用Detailed渲染策略。光照,纹理混合,阴影等均会处理。
    3、距离较近。采用一级顶点索引,使用与情况2相同的Detailed渲染策略。
        最后是水体的渲染。魔兽WLK之前的水体,基本上是采用30张水面轮替形成水波荡漾状来实现的。而在资料片《大灾变》中,加入了实时渲染的水体效果。
        上述魔兽世界地形渲染的基本技巧,尚不能得到实际游戏中那种令人印象深刻的地形效果。影响地形渲染效果的诸多因素当中,光照其实占了很大部分比重,处理好光照模型,渲染出来的地形会有意想不到的效果。正如厨师的秘制配方一样,魔兽地形画龙点睛之处在于高光贴图。
        由于环境光和直射光只能为地形顶点提供无差别的基本明暗效果,反射光作为能根据顶点位置不同而不同的差异性光照,被引入到魔兽地形的光照模型中。而单纯引入反射光又会使得地形看起来像涂了一层釉一样,所以魔兽将地形纹理的Alpha通道,作为其对应的高光贴图,控制该点反射光的强度。值得一提的是,魔兽的水体渲染的光照处理,也采用了与此极为类似的方式。这样的简单技巧已经足够将魔兽的地形渲染提升到像素级光照的精度,实际上,它在魔兽细腻天成的地形效果中,扮演者关键性的作用

----------------------------------------
19. lightmap的支持
对Unity内置lightmap的获取。我们定义两个编译开关,然后在自定义顶点输入输出结构包含lightmap的uv。

#pragma   multi_compile LIGHTMAP_OFF LIGHTMAP_ON  //开关编译选项 
struct v2f
{
   float4   pos : SV_POSITION;
   float3   lightDir : TEXCOORD0;
   float3   normal : TEXCOORD1;
   float2   uv : TEXCOORD2;
   LIGHTING_COORDS(3,   4)
#ifdef   LIGHTMAP_ON
   flost2   uv_LightMap : TEXCOORD5;
#endif
   UNITY_VERTEX_INPUT_INSTANCE_ID

然后在顶点函数中进行如下处理

#ifdef   LIGHTMAP_ON
   o.uv_LightMap   = v.texcoord1.xy * _LightMap_ST.xy + _LightMap_ST.zw;
#endif
最后在像素函数中进行解码处理。

DecodeLightmap函数可以针对不同的平台对光照贴图进行解码。

#ifdef LIGHTMAP_ON  
   fixed3 lm = DecodeLightmap(UNITY_SAMPLE_TEX2D(_LightMap, i.uv_LightMap.xy));
   finalColor.rgb *= lm;
#endif

----------------------------------------

20.模型某个部位高亮,比如手被击中时手高亮(高效地选择局部网格并修改顶点颜色)
    1,2,3比较好,看情况进行选择
    1.建模时在每个需要高亮的部位刷不同的顶点色,shader加入顶点颜色处理
    2.如果是固定部位闪白,可以把要闪白的顶点离线存储下来,或者运行时计算过一次存储在内存中
    3.往shader里传个要高亮的世界坐标,或者包围区域,shader里判断一下顶点是否在区域内
    4.出一张局部遮罩图,与uv对应,如这张图只有左手部分是黑色的,其它部分是白色的。修改shader,可以用这张图与MainTex做运算,实现运行时修改左手部分颜色。
        这个方法多一张贴图的内存消耗
    5.建模时不刷顶点颜色,但是在运行时用脚本修改左手上所有顶点颜色,也是要修改shader加入顶点颜色处理。

----------------------------------------
21.楚留香项目渲染总结
注:
    以下DrawCall写作DC
    此处主要对主城场景的光照效果以及其余效果进行分析
    以下参数的值有很多都是被限定在一定范围内的,应该是一些经过测试的经验值

天气
    雨和打雷
        在相机下挂了个雨粒子,雨涟漪例子
        落下的雨 - 粒子,选择StretchedBillboard模式,粒子会有拉长的效果
        打雷闪光变化 - 白色的粒子,选择Billboard模式,显示时会挡在相机前,就有白色的变化了
        屋顶流水
        地面涟漪 - 分为2部分
            第一部分是人物周围的落点圈,粒子,粒子数很少,是一个从大变小的圈,选择HorizontalBillboard模式
            第二部分是地面涟漪计算,calc_weather_info函数
            这个可以尽量少用涟漪粒子的模拟一个比较好的涟漪效果


        下雪在相机下挂个雪粒子,粒子数量不多,模拟飘雪效果
        建筑和地面的雪通过顶点色处理,雪shader参数如下:
        雪法线强度 - 调节这个参数影响被雪覆盖的物体的细节凹凸感,值越大,凹凸越强
        雪边缘过渡 - 调节雪边缘的柔和度
        雪噪点缩放 - 需要勾上消融雪才会有效果,调节这个值可以控制雪块的大小,值为0时成一块块的雪,且雪和雪之间有部分位置会有少雪的区域过渡,表现消融效果
        雪法线影响调节 - 不勾消融雪才有这个参数,调节这个值可控制雪覆盖的区域,减小这个值瓦片与瓦片之间缝隙会变明显,增大这个值则雪覆盖的区域会变大
        硬边雪 - 雪边缘完全没有柔和,且垂直的顶点也会被设置为白色
        是否接收天气 - 没看出作用
        打开开发者模式
            多出了高光强度贴图,高光粗糙贴图,AO图的选项

太阳耀斑

角色
    角色受光
        为了让角色受光均匀,使用了法线计算3阶球谐作为间接diffuse,使用虚拟光方向,虚拟光强度来和场景光照解耦,再加上自定义的一个pbr高光
        虚拟光只影响角色的皮肤,衣服和武器的光照
        有个环境光调节的参数,可调节角色间接光强度
    角色头发

角色衣服pbr

角色皮肤

角色眼睛

角色披风摆动

sss
        sss只做用于衣服,皮肤没有sss
        SSS只影响到了脸部是通过AO图控制的
    建筑sss的计算

角色在一定范围内可以正常显示,超出范围则显示全白

高模烘培为低模
        不知道这个项目是否有做这个处理

角色阴影
        在光源上挂个相机+ShadowMap.shader渲染深度,生成深度纹理
        然后在shader中比较片段的(深度+bias)和(采用得到的深度纹理),大于深度纹理的则有阴影
        阴影中采用了4次采用的双线性插值PCF

环境
    水
    水/镜面反射
    近处的水

雾/高度雾

后处理
        没开Bloom

全局SH控制

建筑上的n个材质研究(为什么一个建筑需要这么多个材质球)

ao

烘培的光照贴图/以及烘培参数

远景

光线上挂着的阴影贴图
占用3个drawCall

建筑阴影/AO

贴图的制作

整个场景的可调节参数
    角色虚拟光颜色,虚拟光颜色,虚拟光方向 - 只影响角色的皮肤,衣服和武器的光照
    场景漫反射差 - 影响场景漫反射亮度,不影响角色
    场景背光(逆光/轮廓光)颜色/场景背光强度 - 影响场景的体积感,逆光具有很强的塑形感,有利于勾勒轮郭,表现空间层次和营造气氛。
    开启unity原生雾 - 此处选择不开启unity原生雾,使用高度雾,可以使近处的物体,比如树和水也带上雾颜色
    雾初始高度 - 值越大则底部的物体也能看到雾,会有种被雾包围的氛围
    高度雾最远距离 - 值越大雾越低
    高度衰减曲线调节 - 值越大雾减少的越快,表现也是雾变低
    雾高度强度调节 - 值越大雾越高
    雾颜色
    雾透明 - 值越大雾越明显

反射环境色 - 加上这个球携数据,则树,水,建筑上的的雾颜色会消失,远处的雾还存在

远景色 - 这个值控制远处的渲染颜色,和雾颜色是叠加的
    远近变色最远距离 - 控制多远的范围内会加上远景色
    远近变色强度调节
    远近变色曲线调节(远近过渡) - 和远近变色最远距离在效果上没什么区别

雾透明1 - 第二个雾透明参数,影响范围和之前的雾透明一样,但这个雾透明1的颜色加成比较小,之前的雾透明加成比较大
              这个值对颜色的影响比较小,两个值结合起来在表现上则是通过雾透明1调节近处的雾浓度,通过之前的雾透明调节远处的雾浓度

高光剔除 - 值越大,类似栏杆下的阴影,桥上的阴影,屋子房梁遮住的位置的阴影越深,影响范围也越大
    
    远景背光变色 - 没效果
    远景背光强度调节 - 没效果
    雾背光强度调节 - 没效果
    高度雾倍光强度调节 - 没效果
    场景虚拟光颜色 - 没效果
    场景光方向(烘培后用于打高光背光等) - 没效果
    场景光强度 - 没效果

天气参数
    雨法线图 - 雨掉落在地上的波纹细节
    雪噪声图
    是否开启雨
    雨法线强度
    天气颜色 - 影响场景的颜色,模拟雨天环境变暗,效果上这里只影响场景,不影响角色
    高光粗糙度 - 调节这个值影响场景的高光亮度,在一个范围内调节可以模拟打雷时,环境的变色
    是否开启雪
    雪强度
    雪颜色
    雪高光锐度
    消融雪调节
    亮度

镜头光晕

全局分析
    DC
        镜头看着地面 50DC(最少DC的情况)
        镜头看着远处 385DC(最多DC的情况)
        角色+武器 8DC , 存在2个角色时DC不会合并
        远处水 2DC
        近处水 2DC
        动态合并的批次 4-8个        
    内存占用

面数
        78k-404k    顶点数250k-1M
    
    fillrate/OverDraw
        这里分析的场景,这个数值一般在显示粒子特效和ui层级多的时候需要更多的关注

查看FrameDebug,用其他工具看渲染数据

优化
    shader中添加开发模式,在开发者模式下可以编辑以下四种单通道贴图
    "_SpecMap", "_GlossMap", "_AO", "metallic_ctrl_tex" 
    点击保存后,这四张图编码保存在一张Tga的不同通道里
    再次进入开发者模式则从这张Tga里反编码出这四张图
    优点:节省内存,减少采样次数

dc在150以下 内存在270m以下 面数4w面
----------------------------------------
楚留香项目逻辑总结
技能

角色行为

按键重定向

自动重新生成导航网格

选人

资源加载

----------------------------------------
22.阴影
unity中实现:https://zhuanlan.zhihu.com/p/45653702
现实中的阴影分为本影区(umbra,也就是hardShadow)和 处于被部分遮挡形成的半影区(penumbra,也就是softShadow)
为什么需要预渲染阶段
    由于光栅化的渲染管线相比基于光线追踪的实现方式缺少全局性信息,每个 fragment 并不清楚全局的光照情况,无法直接判断自己是否处于阴影中,因此需要额外预渲染阶段。
大体流程
    Shadow mapping由两个绘制过程构成。
    第一个过程,在光源处设置相机绘制场景,深度测试和写入打开,生成shadow map。
    第二个过程,正常绘制场景,把当前片段转换到光源空间,计算得到的深度与采样shadow map得到的深度比较大小,判断当前片段是否在阴影中
Unity中得到第一个过程中的深度图
    1.光源位置创建正交投影相机
    2.需要注意的是深度相机的背景色默认为白色,白色值为1表示无限远。保证后面主相机正常绘制过程中深度比较的正确。
不同光源下阴影的实现以及投影矩阵的选择
    平行光,在投影矩阵的选择上应该采用正交
    平行光和聚光灯(Spot Light)都有固定的方向,而泛光灯(omnidirectional shadow maps)向四面八方发光。
    其实思路都是一致的,只是具体使用 Cubemap 保存 Shadow Map,可以参考:
    https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows
容易碰到的问题:
    1.如果阴影不显示那么可能是精度问题
        解决:
            使用float
    2.深度偏移问题
        物体表面也有阴影效果,也就是Surface acne 或者 self-shadowing现象,原因是 Shadow Map 的分辨率是离散的,多个 fragment会对应到同一个纹素
        具体出现的原因图可参考 https://www.cnblogs.com/BaiPao-XD/p/10527563.html
        解决:
            在比较深度时使用DepthBias进行偏移,这个偏移值过大又会出现"Peter Pan"现象(物体飘在了空中),
            当表面法线与光源方向夹角很大(上图中表面更加倾斜)时还是会出现,最好的做法是根据法线方向和光线方向计算,
            但是考虑到性能,直接为暴露的bias值,范围【0到0.1】
            加上Cull front
    3.抗锯齿问题
        均值模糊PCF
            缺点:
                条状痕迹明显 
                为了得到较好的效果,需要增大滤波核尺寸,相应地也就增加了运算量
        
        双线性插值PCF(手游一般选这个)
            half4 PCF4Samples(float4 shadowCoord)
            {
                float sum = 0;
 
                float depth = DecodeFloat(offset_lookup(_kkShadowMap, shadowCoord,  float2(0.5, 0.5)));
                sum += max(step(shadowCoord.z - _bias, depth), _strength) * 0.25;

depth = DecodeFloat(offset_lookup(_kkShadowMap, shadowCoord,       float2(-0.5,  0.5)));
                sum += max(step(shadowCoord.z - _bias, depth), _strength) * 0.25;

depth = DecodeFloat(offset_lookup(_kkShadowMap, shadowCoord,     float2(-0.5, -0.5)));
                sum += max(step(shadowCoord.z - _bias, depth), _strength) * 0.25;

depth = DecodeFloat(offset_lookup(_kkShadowMap, shadowCoord,     float2( 0.5, -0.5)));
                sum += max(step(shadowCoord.z - _bias, depth), _strength) * 0.25;

return sum ;
            }

Poisson Disk(随机采样 过于规律的块状痕迹还可以采用随机采样的方法缓解)
        生成 Poisson Disk 采样集的算法和具体实现看以下链接
        http://www.cemyuksel.com/cyCodeBase/soln/poisson_disk_sampling.html
        https://bost.ocks.org/mike/algorithms/#sampling
            这是使用已生成好的4个采样点偏移
            const poissonDisk = [
                -0.94201624, -0.39906216,
                0.94558609, -0.76890725,
                -0.094184101, -0.92938870,
                0.34495938, 0.29387760
            ];

uniform float uPoissonDisk[8];
            float PoissonDisk(sampler2D depths, vec2 uv, float compare, float bias) {
                float result = 0.0;
                for (int i = 0; i < 4; i++) {
                result += texture2DCompare(depths, uv + vec2(uPoissonDisk[i * 2], uPoissonDisk[i * 2 + 1])/2048.0, compare, bias);
                }
                return result / 4.0;
            }
        结果还是有"规律"的条状痕迹
        使用叫Stratified Poisson Disk的方法,大体上是就爱上伪随机选取偏移量
            参考:https://github.com/opengl-tutorials/ogl/blob/master/tutorial16_shadowmaps/ShadowMapping.fragmentshader#L20-L37
            伪随机代码:
            float random(vec3 seed, int i) {
                vec4 seed4 = vec4(seed,i);
                float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));
                return fract(sin(dot_product) * 43758.5453);
            }
        之后会发现条状问题解决了,但出现了噪点的问题
        使用Rotated Poisson Disk
            每次旋转一个随机角度得到随机的采样点:
            uniform float uPoissonDisk[8];
            float RotatedPoissonDisk(sampler2D depths, vec2 uv, float compare, float bias){
                float result = 0.0;
                for (int i = 0; i < 4; i++) {
                // 随机角度
                float angle = 2.0 * PI * random(floor(v_Position.xyz * 1000.0), i);
                float s = sin(angle);
                float c = cos(angle);

// 旋转矩阵
                vec2 rotatedOffset = vec2(uPoissonDisk[i * 2] * c + uPoissonDisk[i * 2 + 1] * s,
                uPoissonDisk[i * 2] * -s + uPoissonDisk[i * 2 + 1] * c);

result += texture2DCompare(depths, uv + rotatedOffset/2048.0, compare, bias);
                }
                return result / 4.0;
            }
        效果上来看就是条状消失了,边缘部位会有噪点的感觉

其他的不需要预计算的方法
             CSM(Convolution Shadow Mapping)和 ESM(Exponential Shadow Mapping)
             VSM(Variance Shadow Mapping)不再使用信号处理中的卷积计算滤波核数据,转而从统计角度考虑这个问题,使用灰度值来表达阴影效果。
             PCSS(Percentage Closer Soft Shadows)模拟基于物理的软阴影,能够设置变化的滤波核来改进这一点
        4.大场景远距离阴影问题
            PSSM阴影:先渲染3/N个阴影图,然后在shader里面,根据不同的距离,采样不同的阴影图。难点在于三个阴影图Camera参数的计算
---------------------------

21反射
    https://blog.csdn.net/puppet_master/article/details/80808486
    Unity Shader-反射效果(CubeMap,Reflection Probe,Planar Reflection,Screen Space Reflection)
    https://blog.csdn.net/puppet_master/article/details/81144266
    图形学相关数学(反射,折射公式)
    几种方案
    Reflection Probe
        环境反射+高光+Bloom+HDR套餐,可以做出金属效果
        需要注意反射层级,以及参数的调整
    Box Projection Reflection Probe
        使用Reflection Probe时当反射对象与被反射对象距离较近时,反射效果会出现错误
        可以用一种叫做Box Projection Cube Map的技术,通过重新修正反射采样方向,得到相对较好的效果
        缺点是:
            Box Projection方法在边界有畸变
            反射的范围要和Reflection Probe的范围一致,否则结果不对
        适用:
            室内场景等边界与Reflection Probe边界一致的这种场景,使用这个技术比较合适,可以用一个比较cheap的方案达到近似plannar reflection的效果
        half4 frag (v2f i) : SV_Target
        {
            float3 reflectDir = i.reflectionDir;
            //通过BoxProjectedCubemapDirection函数修正reflectDir
            reflectDir = BoxProjectedCubemapDirection(reflectDir, i.worldPos, unity_SpecCube0_ProbePosition, unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
            half4 rgbm = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, reflectDir);
            half3 color = DecodeHDR(rgbm, unity_SpecCube0_HDR);
            return half4(color, 1.0);
        }
        BoxProjectedCubemapDirection的原理可以参见链接
    平面反射(Planar Reflection)用一个反转的相机再渲染一次场景模拟
        只适合平面,会导致需要反射的物体的DrawCall翻倍
        注意:
            会有裁剪会不对的问题,因为仅改变了顶点,没有改变法向量,绕序反向,所以要在这个相机渲染前设置背面裁剪反向:GL.invertCulling = true;
            为了降低DrawCall,需要设置反射层,将不需要反射的物体排除掉        
    屏幕空间反射(Screen Space Reflection)
---------------------------
    
22.从一边到另一边逐渐透明
    22.1.用坐标,分别用xyz控制每个方向的透明度
    22.2.使用substance painter,给模型制作一个从一段到另一端由白到黑的贴图,把贴图导出,传入shader中,开启透明度测试,对该贴图进行采样,设置一个阈值,当采样当alpha值≤这个阈值时,clip掉。阈值在设定时间内从0增加到1,物体便会从一端到另一端渐隐。如果要半透明渐隐效果,可以关闭深度写入,以采样到的这个贴图透明度为混合系数,对主贴图的采样颜色和缓存中的颜色进行Blend混合,输出。记得开启双面渲染。

---------------------------
23.GPUskin得骨骼矩阵变换
从当前骨骼的bindpos一直左乘到根骨骼
GPU蒙皮
    步骤:
        将一个预设上的所有动画的每一帧的骨骼矩阵写到纹理中,骨骼索引记录到uv2中 (烘培骨骼,先记录骨骼相对关系,骨骼蒙皮矩阵,权重等信息,再根据动画片段和这些骨骼信息,将信息记录到纹理中)
        记录下动画的开始时间和结束时间        
        运行时在shader中根据骨骼索引取出每一个骨骼的蒙皮矩阵,在和骨骼模型空间坐标相乘得到这个骨骼的世界坐标,之后再转到裁减空间
        注意:每个动画写入时前后需要各扩展1帧,否则动画会有闪动
---------------------------
24.后处理全屏水
屏幕坐标转为世界坐标的还原
水面坐标计算
镜面反射
菲涅尔反射与透射
水体次表面散射
水面波纹实现
水面波函数
噪声图

---------------------------

25.卡渲
不受远近距离影响的描边 + Lambert+GGX高光+菲涅尔 + 
色块分层 + 暗部阴影调节 + ao影响阴影 + 次表面散射

---------------------------
26.
体积光:raymarch雾 或者 只受单光源影响的raymarch投射光柱(这两个性能都ok,未验证)

---------------------------
26.阴影
移动端阴影:ShadowMap+PCF+平面阴影
https://zhuanlan.zhihu.com/p/45805097
https://zhuanlan.zhihu.com/p/29959628
https://zhuanlan.zhihu.com/p/92017307
室外大场景阴影贴图分辨率过高的优化
    CSM
    把Camera的可视范围分为多个T型的视锥,每个视锥使用不同分辨率和ShadowMap和不同的处理阴影的Shader
    近处的分辨率高加模糊,远处的分辨率低过渡可以硬一些。如果对质量要求不高还可以简单粗暴的直接降低整体分辨率
    对于静态场景摄像机和光源不更新,远处隔帧更新
    LOD,例如依据远近,重要程度做细分,比较远不重要的物件可以不烘焙阴影,不接受阴影
    只保留重要物件的自阴影
    软阴影采样比较耗,可以降低软阴影的效果,降低过渡的效果,使用其他的软阴影算法,依据远近调整效果
    结合动态阴影和Lightmap,例如近距离使用动态阴影,中远距离使用Lightmap,中间阶段混合过渡
    地形分块的阴影交接处理
    阴影要分层,远中近,远处用最简单的shader不做阴影过滤并且用简模渲染可以间隔几帧刷新
阴影有哪些做法
    距离场阴影    
    软阴影(移动端一般用不上)
        基于Cubemap的动态软阴影
    地面云阴影
        不使用实时灯光对云生成阴影,而是在地面Shader中混合一个运动的云图达到类似效果
    植物摇曳阴影
    建筑阴影
    平面阴影
        https://zhuanlan.zhihu.com/p/31504088
    基于深度的ShadowMap
        https://zhuanlan.zhihu.com/p/45653702
        https://blog.csdn.net/aceyan0718/article/details/52067264
    Projector Shadow(几年前比较流行的做法)
        https://zhuanlan.zhihu.com/p/42433900
        缺点:
            地表会绘制多一次
            地面下的物体也会产生阴影
            多人投影这种方案效率明显低于ShadowMap,很明显地形多画了一遍
            自投影也解决不了
            CommandBuffer批次小于直接用Camera渲染是因为Cmera视椎体没有Fit to view,区域太大导致的,做一个投影相机范围适配即可
            其次用自带的相机会帮你处理合批裁剪
            用主相机的可见性判断的问题,如果有一个房子投影到视椎体内的物体上,但是不在视椎体内就不会产生阴影,这是错的
            阴影精度和范围参数不好调
    Shadow Volume
    自阴影    
    阴影面片
        非关键角色可以使用
    抗锯齿
        阴影锯齿分为透视导致的锯齿(Perspective alias)和投影导致的锯齿(Project alias)
        PCF(Percentage Closer Filtering,柔滑边缘) / PCF双线性插值 
            2种做法:使用随机采样实现soft shadow、泊松采样
            https://zhuanlan.zhihu.com/p/45805097
            https://blog.csdn.net/candycat1992/article/details/8981370
            http://www.ownself.org/blog/2010/percentage-closer-filtering.html
            PCF使用时间种子的随机采样配合Temporal效果更好
        PSM(Perspective Shadow Map)
            透视导致的锯齿是因为透视的近大远小所导致的,psm它将整个Shadow Map的计算过程转到归一化设备空间(NDC)来计算,从而消除了近大远小的问题
            缺陷:PSM本身有很大局限性,比如影子质量比较依赖视角方向、近处阴影与远处阴影Z分布过大
        LISPSM(Light Space Perspective Shadow Maps)
            它是在和灯光方向垂直的方向构建View Frustrum,然后将灯光、场景都转到这个View Frustrum的Perspective space,然后再计算Shadow Map,这样无论是点光、聚光、平行光就都转为平行光
            效果比PSM好
        VSM(方差阴影)
            使用PCF时一般不能提前对Shadow Map进行模糊处理,因为这会导致PCF计算不准,而Variance Shadow Maps则没有这样的限制。VSM存储的Shadow Map不仅包括深度,还有深度的平方,这时可以对Shadow Map做过滤,然后利用切比雪夫不等式计算出大于当前深度的概率上限,也就是阴影区的概率
            http://www.punkuser.net/vsm/
        CSM(Cascaded Shadow Mapping) / PSSM
            Unity用的就是CSM
    注意:
        通过自定义shader直接将深度渲染到纹理来避免使用自带的_CameraDepthTexture,这样可以忽略硬件的限制
        Camera上的RenderTexture的格式选择R8,这个格式创建的贴图内存占用是最小的
        CommandBuffer在某些安卓机上会有问题,把主DepthBuffer清空
        用正交相机渲深度图
        移动平台的浮点纹理问题,可以把0到1的32位浮点压缩到RGBA32
        低配下需要关闭实时阴影
---------------------------
27.卡通水
https://zhuanlan.zhihu.com/p/138083802
https://github.com/IronWarrior/ToonWaterShader
---------------------------
28.偏真实得水

---------------------------
29.水的分解
https://zhuanlan.zhihu.com/p/138083802
浅滩到深水区的颜色渐变
水波纹往岸边推进
水底纹理的扭曲
白色浪花泡沫往岸边推进消失

---------------------------
30.体积光
复杂度1级:BillBoard贴片
    噪声图+遮罩
复杂度2级:降采样+径向模糊
    径向模糊缺点:如果光源不在画面内,径向模糊用不了
复杂度3级:光线追踪+散射函数+阴影
---------------------------
31.体积云
取巧的办法
    GPUInstance绘制n个面片,面片位置在y轴上按层次排列
    模拟云层的融合和分离,采样变化不大的噪点图,噪点图采样的uv会根据时间进行偏移
    模拟云层中部和边部颜色的差异,根据像素进行偏差采样模拟背光和向光面颜色不一样
---------------------------
32.体积雾
---------------------------
33.高度雾 深度雾

----------------------------
34.matcap
matcap+Reflection Cube Map模拟pbr、
使用某特定材质球的贴图,作为当前材质的视图空间环境贴图(view-space environment map),来实现具有均匀表面着色的反射材质物体的显示
将法线从模型空间转换到视图空间,并切换到适合提取纹理UV的区域[0,1]。(需要将法线从模型空间转换到视图空间,推导可以参考:http://www.cnblogs.com/flytrace/p/3379816.html)
----------------------------
35.延迟渲染
    解决了大量光照的渲染消耗问题
    占用大量的显存带宽
        因为GBuffer的RTT至少有2到3张
        为什么说延迟渲染的一个弊端,是占用了大量的显存带宽?随便做一下计算。假设你渲染一个1920 * 1080的全屏,你的GBuffer使用了3个Render Target(最少都要2个,3个)。
        那么一帧的GBuffer需要用到的带宽是:1920 * 1080 * 4 * 3 = 23M。假设你一秒钟渲染60帧,还要乘以60.那么就是23 * 60 = 1.4G。
    只能使用同一个光照pas
    缺点:
        不支持MSAA,只支持FXAA跟TXAA
        不支持透明物体的渲染
    延迟渲染是通过先算出需要着色的像素,然后再迭代灯光,从而减少大量无效的灯光计算,来达到优化的目的
    前向渲染和延迟渲染的区别
        前渲在同一渲染层排序n个mesh时,这些mesh只有z值不同,则最差的情况是需要写入n次深度,做n次的光照计算(因为每次都通过深度测试),会有多次的无效计算
        延渲在同一渲染层排序n个mesh时,这些mesh只有z值不同,则先进行所有像素的深度测试,将深度最小的写入GBuffer,再进行光照计算

-------------
lua
1.lua文件加载完缓存后,调试时修改了lua代码,怎么样可以不重载
    可以通过写GM命令,单独再加载一次这个lua文件

FirClient分析:

[笔记]unity渲染相关各种方案总结相关推荐

  1. [笔记]unity渲染类零碎代码记录(100多条)

    1.获得renderTexture上的4个角的近裁面位置 cam = GetComponent<Camera>(); Matrix4x4 inverseViewProjectionMatr ...

  2. unity 渲染相关优化

    渲染流程 看一下unity几个比较重要的点 drawcall,Batches,SetPass 1.Drawcall:CPU向GPU发送数据绘制图元 DrawCall 只是 Unity 需要推送到 GP ...

  3. 【笔记】unity渲染类名词术语概念总结(30个点)

    1.线性追踪 亮度值是一个空间函数,而光线追踪做的事情其实是求这个函数在视线线段上的线积分 结果为:光线在视线方向上作用值之和 视线上的所有采样点的亮度之和能用来表示光线在介质中散射的效果 2.光线的 ...

  4. Unity基础笔记(5)—— Unity渲染基础与动画系统

    Unity渲染基础与动画系统 Unity渲染基础 一.摄像机 1. 摄像机概念 和现实中的摄像机很接近,Unity 中 Camera 组件负责将游戏画面拍摄然后投放到画面上 Camera 拍摄到的画面 ...

  5. Unity 面试题汇总(三)Unity 基础相关

    Unity 面试题汇总(三)Unity 基础相关 目录 Unity 面试题汇总(三)Unity 基础相关 0.FSM(状态机).HFSM(分层状态机).BT(行为树)的区别 1.什么是协同程序? 2. ...

  6. 关于Unity渲染优化,你可能遇到这些问题

    原文链接:https://blog.uwa4d.com/archives/QA_Rendering.html 关键字 Draw Call 半透明物体渲染 多层纹理渲染 Graphics.Present ...

  7. 从FrameDebugger看Unity渲染

    从FrameDebugger看Unity渲染(一) Unity如何渲染一个3D+2D的游戏画面,今天通过FrameDebugger来看下Unity内置渲染管线的渲染策略, 后续再出一些URP渲染管线相 ...

  8. Unity渲染教程(九):复杂材质 https://www.jianshu.com/p/5e3af869870f

    Unity渲染教程(九):复杂材质 https://www.jianshu.com/p/5e3af869870f 同样的着色器,不同的贴图 用户界面 到目前为止,我们一直都为我们的材质使用Unity默 ...

  9. Unity 渲染 YUV

    YUV和RGB一样,是另一套用来表达颜色的方案.其详细叙述请参阅 YUV的维基.本篇着重讲解如何使用Unity来渲染YUV形式的数据视频或图片.对此,你需要了解以下知识: YUV的数据格式 YUV转换 ...

  10. Unity渲染顺序(2)

    Camera 除了Screen Space - Overlay(屏幕空间覆盖模式)下的Canvas,场景中的其他物体需要渲染到屏幕中,都需要在指定的相机的绘制下.场景中可以创建多个相机,每个相机所拍摄 ...

最新文章

  1. 理解标准输出流方法:WriteLine和Write
  2. 我的Ubuntu9.10安装与配置
  3. SQLite自增关键字报错(near “AUTO_INCREMENT“: syntax error)
  4. C语言扫地雷游戏的题目简介,C语言程序设计课程设计(论文)-扫地雷游戏.doc...
  5. matlab cable,Matlab_Wireless_Communications_example MATLAB通信技术的全部例程(全)包括WCDMA - 下载 - 搜珍网...
  6. xp系统能支持mysql_windowsxp下的mysql集群技术
  7. 寒武纪与华为海思分庭抗礼:中立芯片公司的成人礼
  8. matlab 正态分布相关 API
  9. Amplify Shader Editor手册 Unity ASE(中文版)
  10. Tomcat启动报错记录与千里追踪[持续记录]
  11. 计算机窗口闪退,最近电脑经常会有一个一闪而过的窗口,发现是CONSENT.EXE。请问这样正常吗?...
  12. win10希望计算机做什么取消,主编设置win10设置和取消定时关机的解决形式
  13. 最优化八:高斯牛顿法、LM法
  14. Excel 2010 VBA 入门 052 DateAdd函数
  15. STM32Cube_FW_F4_V1.17 F4固件包百度网盘下载
  16. 采集规则七:河溪小说网 www.518cqdl.com 适用于-易读系统小说站河溪小说网的采集规则
  17. 股票K线几种线型基本规则
  18. 鸿蒙适配倒计时,鸿蒙2.0上线倒计时,华为的最强反击终于来了!
  19. 如何为自己所有的域名注册Google企业邮箱
  20. 云南计算机专修学校附中,2015昆明最强高中排行榜出炉 师大附中拿下第一

热门文章

  1. A/B/C类ip地址 掩码 广播地址 路由汇集 保留地址
  2. 设计模式--C++学习(4)
  3. 【Python实战|小旭学长】使用python进行城市数据分析【完结】
  4. python暴力破解WiFi
  5. linux和windows截图软件下载,【教程】数字菌教你从windows过渡到linux之软件的替换...
  6. map字符串转json格式
  7. SDIO接口(1)——SDIO简介
  8. 小爱同学app安卓版_小爱同学app下载安卓版|语音助手下载_最火软件站
  9. GSCC呼叫中心系统
  10. python数据处理(招聘信息薪资字段的处理)