yukamu 发表于 2022-4-15 08:01

unity简单还原原神水体

最近在看关于原神水体还原的文章,跟着做出来了自己比较满意的结果,就当第一次发文练练手好了
下面是参考大佬的文章的链接(主要参考的是第二篇
<hr/>我总结了下关于水体渲染的细节效果粗略的分为下面几类:


有了这个基本框架,就比较好进行下面的代码编写了
<hr/>一、表面波纹的形成
我这里选择使用的是法线扰动,暂时还没有去了解顶点动画的方法,算是留个坑等到时候学习的时候再补
其实法线扰动的方法也不是特别好看,至少近看一下就出戏了,这里缩放了法线的效果,不然看上去真的不太行。原文章用的是柏林噪声的方法同时对法线、反射来进行扰动,下次可以去试试那种方法
法线扰动的思路:使用一张或两张法线贴图,在采样的时候通过uv坐标偏移的方式来扰动得到的结果,使法线进行xz轴的偏移。使用两张法线贴图的话只需要一个寄存器存储第一张法线贴图,然后进行uv偏移后采样第二张贴图,得出的结果相加就可以得到比单纯的一张贴图的效果好。
所以我们要在属性面板中添加两个贴图,同时使用一个变量来控制法线的凹凸强度
_NormalMap1("第一张法线贴图",2D) = "bump"{}
_NormalMap2("第二张法线贴图",2D) = "bump"{}
_NormalScale("凹凸强度",Range(0,1)) = 0.5
然后进行贴图的采样,得到后的结果作为法线方向normalDir(这里我偷懒了直接用的大佬写好的代码,因为和我自己写出来的差不多,而且直接写出来独立成为函数可以减少些代码量(划掉),使用了_FlowSpeed作为扰动速度(水流流动速度)
float3 SampleNormal(sampler2D normalMap,float2 uv)
{
float4 packedNormal = tex2D(normalMap, uv);
float3 waterNormal = UnpackNormal(packedNormal);
return float3(waterNormal.x * _NormalScale,waterNormal.z,waterNormal.y * _NormalScale);   
}

float3 SampleWaterNormal(float2 uv)
{
float2 velocity = float2(1,0) * 0.02;
float t = _Time.y *_FlowSpeed;
float3 n1 = SampleNormal(_NormalMap1,(uv + velocity.yx * t* 1.2) * 1.5);
float3 n2 = SampleNormal(_NormalMap2,(uv + velocity.xy * t ));
float3 n = n2 + n1;
n = normalize(n);
return n;
}

float3 normalDir = normalize(SampleWaterNormal(i.uv_NormalMap));//在像素着色器中把法线的uv坐标传进去这样我们就得到了扰动好了的法线,第一部分完成了
二、水体颜色
关于这部分,我们使用的是基于深度进行着色(深度越深,颜色越深)
给水体上色有多种方法。可以自定义深水区颜色和浅水区颜色,然后根据深度进行线性差值(lerp)。也可以使用ramp图的方法进行着色。还可以使用颜色函数得到混合后的颜色(这个可以参考挂的第一个链接,里面有网站能直接生成渐变色的函数,因为我自己调的颜色不太好看,所以就直接拿原文的函数了)
1.获取深度:
//对_CameraDepthTexture进行采样,得到非线性的深度(需要在pass中包含#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl")
float sceneZ = SAMPLE_TEXTURE2D(_CameraDepthTexture, sampler_CameraDepthTexture, i.screenPos.xy/i.screenPos.w);
//再将非线性的深度转化成线性的深度,这个变量就存储了渲染好的场景的深度
sceneZ = LinearEyeDepth(sceneZ,_ZBufferParams);
//然后创建变量存储当前渲染像素点(ShadingPoint)的深度
float partZ = i.screenPos.w;
//diffZ的值是当前像素点和场景的像素的差值,因为我们需要根据深度进行着色,所以需要根据两者的差值进行计算
//差值越大表明深度越深,颜色越深,差值越小表明深度越浅,颜色越浅
float diffZ = (sceneZ - partZ) /5.0;这样子我们就得到了两种深度以及他们的差值,可以进行着色操作了
2.基于深度着色、控制透明度
//利用cos生成的渐变色,使用网站:https://sp4ghet.github.io/grad/
float4 cosine_gradient(float x,float4 phase, float4 amp, float4 freq, float4 offset){
float TAU = 2. * 3.14159265;
phase *= TAU;
x *= TAU;
return float4(
    offset.r + amp.r * 0.5 * cos(x * freq.r + phase.r) + 0.5,
    offset.g + amp.g * 0.5 * cos(x * freq.g + phase.g) + 0.5,
    offset.b + amp.b * 0.5 * cos(x * freq.b + phase.b) + 0.5,
    offset.a + amp.a * 0.5 * cos(x * freq.a + phase.a) + 0.5
    );
}

//在像素着色器中进行计算
//渐变色相关,这四个值是根据上面的渐变色函数所需要的,详情看网站
const float4 phases = float4(0.28, 0.50, 0.07, 0);//周期
const float4 amplitudes = float4(4.02, 0.34, 0.65, 0);//振幅
const float4 frequencies = float4(0.00, 0.48, 0.08, 0);//频率
const float4 offsets = float4(0.00, 0.16, 0.00, 0);//相位

float4 cos_grad = cosine_gradient(saturate(1.5-diffZ), phases, amplitudes, frequencies, offsets);
//clamp():如果 x 值小于 a,则返回 a;如果 x 值大于 b,返回 b;否则,返回 x
cos_grad = clamp(cos_grad, 0, 1);
float3 watercolor = cos_grad.rgb;这样我们就得到了根据深度着色的结果如下


然后可以进一步根据深度控制水体的透明度,同时开启混合模式Blend SrcAlpha OneMinusSrcAlpha、
//saturate函数可以很好的根据深度差值来控制颜色的透明度,即深度越大透明度越深(watercolorAlpha=1),深度越小透明度越小(watercolorAlpha=0)
//saturate():如果 x 小于 0,返回 0;如果 x 大于 1,返回1;否则,返回 x
float watercolorAlpha = saturate(diffZ/_AlphaIntensity);得到的结果如下


三、高光效果
高光往下的部分需要准备计算所需要的向量,后面就进行不描述了
//获取相关向量,urp中获取光照方向的方式
Light light = GetMainLight();
float3 lightDir = normalize(TransformObjectToWorldDir(light.direction));
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
float3 halfDir = normalize(viewDir + lightDir);
float3 normalDir = normalize(SampleWaterNormal(i.uv_NormalMap));
float3 reflectDir = normalize(reflect(-viewDir,normalDir));
float2 screenUV = i.pos.xy * (_ScreenParams.zw - 1);

//这个值是世界空间下的法线和视角方向的点积,因为需要边缘光的效果,这个部分要使用世界空间下的法线方向,如果使用的是变换后的法线方向不能得到“边缘”的效果
float ndotvWS = dot(viewDir,i.worldNormal);
float ndotv = dot(viewDir,normalDir);
float ndoth = dot(normalDir,halfDir);高光直接使用blinnphong模型,不多赘述,需要在属性中添加_Gloss用于控制高光范围和_SpecularIntensity用于控制高光强度
float3 specular = pow(saturate(ndoth),_Gloss) * _SpecularIntensity;高光的结果如下


四、反射效果
反射效果我只使用了天空盒,因为暂时还没有接触其他的反射实现方法(不禁感慨:我好菜啊啊
为了添加反射效果,我们需要在属性面板加入_FresnelPow用于控制菲涅尔强度(多bb两句:因为只是想练练手,没有更加深入的了解水体基于物理的渲染,所以这里没有把水体的菲涅尔强度定死,可以根据自己的想法调整)、_CubeMap用于添加天空盒(没有使用unity内置的变量访问当前的天空盒,那个更改天空盒或者第一次使用的时候需要烘焙一下不然是全黑的天空盒,就直接自定义了)以及一个变量_CubeMapMip用于控制天空盒的mip等级
//使用的是texCUBElod函数进行对天空盒的采样
//至于mipmap有没有生效...我也不太清楚,我这里用的cubemap调mip等级都没有效果...可能是这个天空盒不支持吧
float3 reflectSkybox =texCUBElod(_CubeMap,float4(reflectDir,_CubeMapMip));
float fresnel = lerp(pow((1 - ndotv),5),1,_FresnelPow);就可以得到下面的效果了(这里注意一下不能简单的将上面三个的结果相加,会得到不正确的结果,刚写完那会直接一股脑叠加会过曝)
反射天空盒的结果如下



正确的结果



specular + watercolor + reflectSkybox,过曝的结果

我们可以使用lerp函数将watercolor和reflectSkyBox用fresnel变量进行控制,得到正确的结果



可以看到右上角已经有菲涅尔的效果了,颜色也是正常的

五、折射效果
1.获取_CameraOpaqueTexture
折射效果要求我们得到一张场景中渲染好的texture,内置管线可以使用grabpass得到,urp管线需要在asset中设置OpaqueTexture开启


然后我们就可以访问到_CameraOpaqueTexture和_CameraColorTexture了,然后声明一下sampler2D _CameraOpaqueTexture;就可以使用这个贴图的结果了
2.扰动_CameraOpaqueTexture和采样
这里需要屏幕uv,urp中获取的方法如下,使用了_ReflectIntensity变量来控制折射效果(扰动强度)
float2 screenUV = i.pos.xy * (_ScreenParams.zw - 1);
float2 refractionUV = screenUV + normalDir.xz * _ReflectIntensity;
float4 refractionTex = tex2D(_CameraOpaqueTexture,refractionUV);如果定义和使用的是_CameraColorTexture有可能会出现面片全黑或者是根本没有得到扰动结果,整个面片就是空的。我之前使用的就是这个texture,找了老半天(搜索如何获得_CameraColorTexture什么的根本搜不到结果啊喂)终于搜到了相关的问题定位以及解决方法
这个文章的评论区也有相似的情况,原因是两张贴图的抓取时间不一样,大概是这样的
_CameraOpaqueTexture 是在AfterRenderingOqaque 后抓取的图,_CameraColorTexture 应该是在后处理前。解决了这个问题,输出后就能看到结果了


那么问题来了,反射和折射两者都需要菲涅尔效果、以及前面的高光、水体颜色等要怎么混合才能得到正确的结果呢?(虽然这个问题其实并不难,仔细想想就能得到比较好的解决方案)
在我思考了半天之后,得出结果,两者并不是简单的线性叠加,同时两者线性差值后再进行叠加又会得到过曝的不正确的结果
得出结论:先将折射结果和反射结果相加,再和水体颜色进行线性差值(使用菲涅尔项进行控制)这样就能得到比较完美的结果了
float3 refractAndReflect = reflectSkybox + refractionTex;
float3 GetWaterColor = lerp(watercolor,refractAndReflect,fresnel);
float3 finalcol = GetWaterColor +specular;得到了这样的结果


原文章其实是没有加入折射效果的,我只是单纯为了好玩把这个加进去了,但是会使远处本该不透明的水体变成了半透明的效果,怎么说呢,虽然得到了并不是很正确的结果,但是它好看啊,那这样的话我就懒得去解决这个问题了(其实是肝了几天不太想继续在这方面花时间了,感觉不是很必要解决这个问题(应该是lerp那里有点问题,毕竟反射和折射的结果直接两两相加再和水体颜色进行插值太暴力了),没有什么差强人意的结果就放这里好了)
六、边缘光
边缘光效果我这里使用的是视角方向和法线方向的夹角进行的判断,原文用的是ddx和ddy的方法,效果应该是比我这个要好,使用了_RimColor用于控制边缘光颜色,_RimIntensity用于控制边缘光强度,_RimWidth用于控制边缘光宽度
//边缘光相关
float rim = 1.0 - saturate(ndotvWS);//法线与视线垂直的地方边缘光强度最强
rim = smoothstep(1-_RimWidth, 1, rim);
rim = smoothstep(0, _RimSmoothness, rim);
float4 Rimresult = rim * _RimColor * _RimIntensity;因为水面上看不太清这个具体的效果,就直接用材质球了


七、白沫效果
好吧,这个部分是我觉得本篇中做的比较差的一个效果了,要好看没好看(原神那白沫也不是你这样的啊喂),主要是没怎么弄明白原文中的关于白沫的实现,就自己写了个超级垃圾的效果,建议跳过(划掉
思路是做出一个周期变化的白沫,然后根据深度控制白沫的透明度,大概就这样。然后增加了一个变量_EdgeWidth用于控制白沫的边缘以及_FoamFlowSpeed用于控制白沫的流动速度(本来用的是水体的流动速度,但是那个太快了...),代码基本上都写足了注释,基本还是参考原文,使用的也是原文给的纹理
//这个dis变量是用来控制白沫的周期性变化

half dis = lerp(0.7,1,2-sin(_Time.y * _FoamFlowSpeed));

//进行uv采样的偏移,同时使用_FlowSpeed属性控制流动速度

i.uv_Foam.y -= -(_Time.y-0.2) * _FoamFlowSpeed;
half4 foamTexCol = tex2D(_FoamTex,i.uv_Foam);

//foamCol的三个操作:因为使用的纹理的rg通道有值,所以这里使用rg通道,从而得到白沫的效果
//这里* saturate(_EdgeWidth-diffZ)的操作是为了根据深度控制白沫的有无,深度深->没有泡沫
//这里/dis的操作是用来控制白沫的周期性变化,/dis可以得到白沫从小到比较大的效果,如果使用的是*diss则可以得到白沫从比较大到大的效果

half4 foamCol = (foamTexCol.r+ foamTexCol.g ) * saturate(_EdgeWidth-diffZ) /dis;

//这个smoothstep的操作是为了控制白沫的数量(连续),smoothstep可以得到平滑的结果

foamCol = smoothstep(0.7,1,foamCol) ;

//根据深度控制白沫的透明度,
foamCol.a = saturate(diffZ/_AlphaIntensity);
<hr/>本篇做下来就是这个效果了,算是弄懂了参考原文+额外的扩展一部分。


文章到这里就结束了,还是留了不少的坑没有填(ssr、sspr什么的都没有去学,焦散效果也没有加上去)可能文章也有不少问题我暂时没有发现,欢迎大家指出错误。
希望写文章的话能够鞭策自己不断学习吧,争取在今年寒假找到一份实习
本篇源码如下
Shader "Unlit/CustomWater"
{
    Properties
    {
      _NormalMap1("第一张法线贴图",2D) = "bump"{}
      _NormalMap2("第二张法线贴图",2D) = "bump"{}
      _NormalScale("凹凸强度",Range(0,1)) = 0.5

      _Gloss("高光范围",Range(0,256)) = 8.0
      _SpecularIntensity("高光强度",Range(0,1.5)) = 0.5

      _FlowSpeed("流动速度",Range(0,2)) = 1
      _FresnelPow("菲涅尔",Range(0,1)) = 0.5
      _CubeMap("反射天空盒",cube) = "_SkyBox"{}
      _CubeMapMip("天空盒mip等级",Range(0,8)) = 0

      _FoamTex("白沫贴图",2D) = "gray"{}
      _FoamFlowSpeed("白沫流动速度",float) = 1
      _EdgeWidth("边缘深度",float) = 0.5

      
      _RimWidth("边缘光宽度",Range(0,1)) = 0.5
      _RimColor("边缘光颜色",color) = (1,1,1)
      _RimIntensity("边缘光强度",Range(0,1)) = 0.5
      _RimSmoothness("边缘光平滑",Range(0,1)) = 0.1
      _AlphaIntensity("边缘透明距离",Range(0.1,2)) = 0.5

      _ReflectIntensity("折射强度",Range(0,0.5)) = 0.02
    }
    SubShader
    {
      Tags{"RenderPipeline"="UniversalRenderPipeline"}
      pass
      {
            Tags{"LightMode" = "UniversalForward" "RenderType"="Trasnparent" "RenderQueue"="Trasnparent"}
            
            //Blend One Zero
            Blend SrcAlpha OneMinusSrcAlpha
            //Blend OneMinusDstColor One
            //BlendOp Max
            ZWrite Off
            Cull Back
            
            HLSLPROGRAM

                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

                CBUFFER_START(UnityPerMaterial)
                sampler2D _NormalMap1;
                sampler2D _NormalMap2;
                float4 _NormalMap1_ST;
                float4 _NormalMap2_ST;
                half _NormalScale;

                half _Gloss;
                half _FlowSpeed;
                half _SpecularIntensity;
                samplerCUBE _CubeMap;

                sampler2D _FoamTex;
                float4 _FoamTex_ST;
                float _FresnelPow;
                half _FoamFlowSpeed;
                half _CubeMapMip;
                half _EdgeWidth;

                half _RimIntensity;
                half4 _RimColor;
                half _RimSmoothness;
                half _RimWidth;
                half _AlphaIntensity;

                sampler2D _CameraOpaqueTexture;
               
                half _ReflectIntensity;
                CBUFFER_END

                #pragma vertex vert
                #pragma fragment frag

                struct vertexInput
                {
                  float3 vertex : POSITION;
                  float3 normal : NORMAL;
                  float4 tangent : TANGENT;
                  float2 uv_NormalMap : TEXCOORD0;
                  float2 uv_Foam : TEXCOORD1;   
                };

                struct vertexOutput
                {
                  float4 pos : SV_POSITION;
                  float3 worldNormal : TEXCOORD0;
                  float3 worldPos : TEXCOORD1;
                  float2 uv_NormalMap : TEXCOORD2;
                  float4 screenPos : TEXCOORD3;
                  float2 uv_Foam : TEXCOORD4;
                };

                vertexOutput vert(vertexInput input)
                {
                  vertexOutput output;
                  output.pos = TransformObjectToHClip(input.vertex.xyz);
                  output.worldNormal = TransformObjectToWorldNormal(input.normal);
                  output.worldPos = TransformObjectToWorld(input.vertex.xyz);
                  output.uv_NormalMap = TRANSFORM_TEX(input.uv_NormalMap,_NormalMap1);
                  output.screenPos = ComputeScreenPos(output.pos);
                  output.uv_Foam = TRANSFORM_TEX(input.uv_Foam,_FoamTex);
                  return output;
                }

                          //利用cos生成的渐变色,使用网站:https://sp4ghet.github.io/grad/
                float4 cosine_gradient(float x,float4 phase, float4 amp, float4 freq, float4 offset){
                  float TAU = 2. * 3.14159265;
                  phase *= TAU;
                  x *= TAU;

                  return float4(
                        offset.r + amp.r * 0.5 * cos(x * freq.r + phase.r) + 0.5,
                        offset.g + amp.g * 0.5 * cos(x * freq.g + phase.g) + 0.5,
                        offset.b + amp.b * 0.5 * cos(x * freq.b + phase.b) + 0.5,
                        offset.a + amp.a * 0.5 * cos(x * freq.a + phase.a) + 0.5
                  );
                }
               
                float3 SampleNormal(sampler2D normalMap,float2 uv)
                {
                  float4 packedNormal = tex2D(normalMap, uv);
                  float3 waterNormal = UnpackNormal(packedNormal);
                  return float3(waterNormal.x * _NormalScale,waterNormal.z,waterNormal.y * _NormalScale);   
                }

                float3 SampleWaterNormal(float2 uv)
                {
                  float2 velocity = float2(1,0) * 0.02;
                  float t = _Time.y *_FlowSpeed;
                  float3 n1 = SampleNormal(_NormalMap1,(uv + velocity.yx * t* 1.2) * 1.5);
                  float3 n2 = SampleNormal(_NormalMap2,(uv + velocity.xy * t ));
                  float3 n = n2 + n1;
                  n = normalize(n);
                  return n;
                }

                float4 frag(vertexOutput i):SV_TARGET
                {
                  //获取相关向量,urp中获取光照方向的方式
                  Light light = GetMainLight();
                  float3 lightDir = normalize(TransformObjectToWorldDir(light.direction));
                  float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
                  float3 halfDir = normalize(viewDir + lightDir);
                  float3 normalDir = normalize(SampleWaterNormal(i.uv_NormalMap));
                  float3 reflectDir = normalize(reflect(-viewDir,normalDir));
                  float2 screenUV = i.pos.xy * (_ScreenParams.zw - 1);

                  //这个值是世界空间下的法线和视角方向的点积,因为我们需要边缘光的效果,这个部分要使用世界空间下的法线方向,如果使用的是变换后的法线方向不能得到“边缘”的效果
                  float ndotvWS = dot(viewDir,i.worldNormal);
                  float ndotv = dot(viewDir,normalDir);
                  float ndoth = dot(normalDir,halfDir);
                  
                  //获取深度
                  //我们需要对深度图_CameraDepthTexture进行采样,后面的xy/w的操作是为了得到正确的结果
                  float sceneZ = SAMPLE_TEXTURE2D(_CameraDepthTexture, sampler_CameraDepthTexture, i.screenPos.xy/i.screenPos.w);
                  //将非线性的深度转换成线性的深度
                  sceneZ = LinearEyeDepth(sceneZ,_ZBufferParams);
                  //partZ的值是当前像素点(ShadingPoint)的深度
                  float partZ = i.screenPos.w;
                  //diffZ的值是两者渲染的当前像素点和场景的像素的差值,因为我们需要根据深度进行着色,同时需要得到渐变的结果。所以需要两者的差值进行计算
                  float diffZ = (sceneZ - partZ) /5.0;
                  //原文中这个比值是用来解决摄像机拉远而产生的问题(白沫增加等),但是这里貌似没有碰到这个问题,所以这个值没有在本篇中出现
                  //float ratioZ = sceneZ/partZ;

                  //渐变色相关,这四个值是根据上面的渐变色函数所需要的
                  const float4 phases = float4(0.28, 0.50, 0.07, 0);//周期
                  const float4 amplitudes = float4(4.02, 0.34, 0.65, 0);//振幅
                  const float4 frequencies = float4(0.00, 0.48, 0.08, 0);//频率
                  const float4 offsets = float4(0.00, 0.16, 0.00, 0);//相位

                  //按照距离海滩远近叠加渐变色
                  float4 cos_grad = cosine_gradient(saturate(1.5-diffZ), phases, amplitudes, frequencies, offsets);
                  //clamp():如果 x 值小于 a,则返回 a;如果 x 值大于 b,返回 b;否则,返回 x
                  cos_grad = clamp(cos_grad, 0, 1);
                  float3 watercolor = cos_grad.rgb;
                  //saturate函数可以很好的根据深度差值来控制颜色的透明度,即深度越大透明度越深(watercolorAlpha=1),深度越小透明度越小(watercolorAlpha=0)
                  //saturate():如果 x 小于 0,返回 0;如果 x 大于 1,返回1;否则,返回 x
                  float watercolorAlpha = saturate(diffZ/_AlphaIntensity);

                  //白沫相关
                  //这个dis变量是用来控制白沫的周期性变化
                  half dis = lerp(0.7,1,2-sin(_Time.y * _FoamFlowSpeed));
                  //进行uv采样的偏移,同时使用_FoamFlowSpeed属性控制流动速度
                  i.uv_Foam.y -= -(_Time.y-0.2) * _FoamFlowSpeed;
                  half4 foamTexCol = tex2D(_FoamTex,i.uv_Foam);
                  //foamCol的三个操作:因为使用的纹理的rg通道有值,所以这里使用rg通道,从而得到白沫的效果
                  //这里* saturate(_EdgeWidth-diffZ)的操作是为了根据深度控制白沫的有无,深度深->没有泡沫
                  //这里/dis的操作是用来控制白沫的周期性变化,/dis可以得到白沫从小到比较大的效果,如果使用的是*diss则可以得到白沫从比较大到大的效果
                  half4 foamCol = (foamTexCol.r+ foamTexCol.g ) * saturate(_EdgeWidth-diffZ) /dis;
                  //这个smoothstep的操作是为了控制白沫的数量(连续),smoothstep可以得到平滑的结果
                  foamCol = smoothstep(0.7,1,foamCol) ;
                  //根据深度控制白沫的透明度,
                  foamCol.a = saturate(diffZ/_AlphaIntensity);

                  //边缘光相关
                  float rim = 1.0 - saturate(ndotvWS);//法线与视线垂直的地方边缘光强度最强
                  rim = smoothstep(1-_RimWidth, 1, rim);
                  rim = smoothstep(0, _RimSmoothness, rim);
                  float4 Rimresult = rim * _RimColor * _RimIntensity;

                  //反射、高光
                  //这个使用的是blinnphong模型
                  float3 specular = pow(saturate(ndoth),_Gloss) * _SpecularIntensity;
                  //这里使用的是texCUBElod函数,至于mipmap有没有生效...我也不太清楚,我这里用的cubemap调mip等级都没有效果...
                  float3 reflectSkybox =texCUBElod(_CubeMap,float4(reflectDir,_CubeMapMip));
                  float fresnel = lerp(pow((1 - ndotv),5),1,_FresnelPow);

                  //折射
                  float2 refractionUV = screenUV + normalDir.xz * _ReflectIntensity; //*0.02
                  //关于_CameraColorTexture得到的结果全黑的解决方法:https://www.bilibili.com/read/cv6504414/
                  float4 refractionTex = tex2D(_CameraOpaqueTexture,refractionUV);

                  float3 refractAndReflect = reflectSkybox + refractionTex;
                  float3 GetWaterColor = lerp(watercolor,refractAndReflect,fresnel);
                  float3 finalcol = GetWaterColor +specular + Rimresult + foamCol;

                  return float4(finalcol,watercolorAlpha);
                }
            ENDHLSL
      }
    }
}
页: [1]
查看完整版本: unity简单还原原神水体