【unity shader】水体渲染基础-水下透视效果

这篇具有很好参考价值的文章主要介绍了【unity shader】水体渲染基础-水下透视效果。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

接下来是水体渲染基础的最后一篇,通过水面看到水下的物体,并呈现深度效果。

1. 搭建简单演示场景

我们直接搭一个小场景。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
增加水面,赋予uv变形的水面材质,并增加透明度的设置。

unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染

SubShader
    {
        Tags { "RenderType"="Transparent" "Queue" = "Transparent" }
        LOD 100

        Pass
        {
            //Tags {"LightMode" = "ForwardBase"}

            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            //.......返回的color结果,添加一个控制透明度的参数
         }
         //注意FallBack也要注释掉
    }

2. 基于雾效实现水深效果

水体会吸收光线,所以真实的水体并不是完全透明的。此外,水体对不同频率的光吸收率不同,蓝光被吸收最少。
故深度越深,水中的物体就会变成蓝色。

我们当然可以直接上一个全局雾,但这里我们最好还是使用仅面向水体的雾效计算。

这里开始,我们新增一个水下计算相关的cginc和一个返回水下片元颜色结果的函数。
新建cginc文件时我们需要注意,在windows文件夹下创建txt文件,并注意要修改文件后缀。

#include "LookingThroughWater.cginc"
float3 ColorBelowWater () 
{
	//目前只返回黑色
    return 0;
}

//返回值乘以ColorBelowWater()的结果,透明度调整为1

首先我们定义了最简单的水下片元颜色计算,得到了石油一般的流体
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
那么要计算深度雾,首先我们需要一个摄像机深度贴图。

// in xxx.cginc
sampler2D _CameraDepthTexture;

另外,在着色器计算时,我们需要获取对应的屏幕空间坐标。

在surface shader里面加入screenPos:

struct Input 
{
	float2 uv_MainTex;
	float4 screenPos;
};void surf (Input IN, inout SurfaceOutputStandard o) 
{
	…
	
	o.Albedo = ColorBelowWater(IN.screenPos);
	o.Alpha = 1;
}

在unlit shader里面,我们可以在片元着色器中增加VPOS语义,实现平面空间坐标的引入。

由于VPOS和SV_POSITION无法在同一个v2f结构中存在,所以我们必须删去原有的v2f中的SV_POSITION,并使之在顶点着色器的参数中通过out语义单独输出。

struct v2f
{
    float2 uv : TEXCOORD0;
    //......
    // float4 vertex : SV_POSITION;
};

v2f vert (appdata_tan v, out float4 vertex : SV_POSITION)
{
    v2f o;
    vertex = UnityObjectToClipPos(v.vertex);
    //.......
    
    return o;
}

fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
{
    //....
}

同样的,我们也可以直接在进行计算,那样就无需使用VPOS,也不需要删去v2f结构体中的SV_POSITION定义。

v2f vert (appdata_tan v)
{
    v2f o;
    
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.screenpos = ComputeScreenPos(o.vertex);

    //.......
}

2.1 获取水下片元到水面的距离

逻辑不难,即是通过水底的片元深度-水面的片元深度,求解对应片元水体的厚度。

float3 ColorBelowWater (float4 screenPos) 
{
    float2 uv = screenPos.xy / screenPos.w;
    
    float backgroundDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv));
	float surfaceDepth = UNITY_Z_0_FAR_FROM_CLIPSPACE(screenPos.z);

    float depthDifference = backgroundDepth - surfaceDepth;
    
    //除以二十,拉开层次差别,这个20的常量,我们可以理解为最大深度
    //所有常量,最好都根据实际搭的场景深度,进行灵活调整
	return depthDifference/20;
}

注意这里片元着色器的返回值换成了纯ColorBelowWater() 的结果。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
若此时我们得到了黑白颠倒的结果,则可能是深度贴图的v坐标是从上到下计算的。对这种情况,我们需要对v维度的uv进行取反。

//in xxxx.cginc
flaot4 _CameraDepthTexture_TexelSize;

float3 ColorBelowWater (float4 screenPos) 
{
    float2 uv = screenPos.xy / screenPos.w;
    #if UNITY_UV_STARTS_AT_TOP
		if (_CameraDepthTexture_TexelSize.y < 0) {
			uv.y = 1 - uv.y;
		}
	#endif
    
    //.........
}

2.2 获取水底渲染帧缓冲

解决了深度信息的计算,新的问题又来了,我们直接把深度信息乘算已有的结果的话,无法正确反映水下的颜色信息。

//in frag shader
return fixed4((col * _BaseColor + diffuse + specular)* ColorBelowWater(i.screenpos), _AlphaScale);

unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
当然我们可以自作聪明地去调整alpha值来稀释黑色效果,但随之而来又会直接破坏深度效果。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
所以我们需要将原本的水下渲染的颜色结果,和深度结算的结果进行差值混合。

因为原本的水体shader计算的只有水面的颜色结果,所以我们铁定是不可能在单个pass里面完成混合了。

我们单独增加一个GrabPass ,提前存储其他物体渲染的结果。由于透明物体渲染顺序本身就在非透明物体后,如果想面向非透明物体获取GrabPass,要注意渲染顺序问题。

根据unity文档的描述,grabpass只能抓取帧缓冲信息,拥有两种调用方法。在不提供目标贴图时,结果会被存储到_GrabTexture;而用户需要指定grabpass输出的暂存贴图时,需要通过双引号给出。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染

SubShader
    {
        Tags { "RenderType"="Transparent" "Queue" = "Transparent" }
        LOD 100
		
		//增加一个GrabPass,将背景物体渲染的结果预先存储到_WaterBackground中,供后续颜色插值混合使用
        GrabPass {"_WaterBackground"}

        Pass
        {
            //水体渲染pass
        }
    }

//in xxx.cginc

float3 ColorBelowWater (float4 screenPos) 
{
    float2 uv = screenPos.xy / screenPos.w;
    #if UNITY_UV_STARTS_AT_TOP
		if (_CameraDepthTexture_TexelSize.y < 0) {
			uv.y = 1 - uv.y;
		}
	#endif
    
    float3 backgroundColor = tex2D(_WaterBackground, uv).rgb;

    return backgroundColor;
}

可以看到在满alpha的情况下,也有了透视的效果。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染

2.3 完成背景渲染和水深的插值混合

在属性中新增两个雾效相关的参数:

_WaterFogColor ("Water Fog Color", Color) = (0, 0, 0, 0)
_WaterFogDensity ("Water Fog Density", Range(0, 2)) = 0.1

根据水底颜色(雾颜色),背景颜色进行差值混合,插值因子是雾浓度和深度的结合。

//in xxx.cginc
// update ColorBelowWater( )
float3 backgroundColor = tex2D(_WaterBackground, uv).rgb;
float fogFactor = exp2(-_WaterFogDensity * depthDifference);

return lerp(_WaterFogColor, backgroundColor, fogFactor);

unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
现在,我们可以通过_WaterFogDensity 来控制水的散射效果,实现水体的深度差别。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
然后对alphascale值做调整,显然现在我们不需要一个控制输出颜色透明度的参数,而是需要一个控制水体是否为透明深度渲染的参数。

原本的alphascale主要用于控制片元着色器颜色计算结果的透明度。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
现在我们将其调整,让其影响fogFactor最终的计算结果,并使片元着色器的返回的w值固定为1

//in .cginc ColorBelowWater
return lerp(_WaterFogColor, backgroundColor, fogFactor * _AlphaScale);

与调整_WaterFogDensity 的效果不同,调节_AlphaScale主要影响是否进行深度混合效果的计算。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染

3.实现水下物体的扭曲

有生活经验的朋友们都知道,水下的物体在有水波时,会出现扭曲现象,参考下图中水下鱼类的边缘,随着水波出现了一定程度的扭曲。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
实现的逻辑不复杂,即是让水下的部分的采样uv,沿水波的法线方向(x,z方向)做偏移即可。

//额外新增了参数_RefractionStrength,用于控制采样水下颜色结果的uv的偏移程度
float3 ColorBelowWater (float4 screenPos, float3 worldNormal) 
{
    float2 uvoffset = worldNormal.xz * _RefractionStrength;
    float2 uv = (screenPos.xy + uvoffset) / screenPos.w;
    //.........
}

水下扭曲现象从无到有的对比
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染

3.1 修正水上物体的误偏移

看上面的代码就知道,由于uv偏移是全局性的计算,所以会导致很多明明没有位于水下的物体,其对应的水面也产生了扭曲颜色的情况。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
修正的方法很简单,就是我们经判断后,只对水下的片元做uv偏移即可,对于水上的uv,则沿用原本的uv,并注意需要重算depthDifference。

float3 ColorBelowWater (float4 screenPos, float3 worldNormal) 
{
    //......新增一个originUV,用于存储偏移前的uv

    if(depthDifference < 0)
    {
        uv = originUV;
        #if UNITY_UV_STARTS_AT_TOP
		if (_CameraDepthTexture_TexelSize.y < 0) {
			uv.y = 1 - uv.y;
		}
	    #endif
	    //使用偏移前的uv采样颜色缓冲,会导致偏移后的uv采样的深度差,与颜色不匹配
	    //这里同时重采样backgroundDepth,再算一次depthDifference 
        backgroundDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv));
        depthDifference = backgroundDepth - surfaceDepth;
    }

    float3 backgroundColor = tex2D(_WaterBackground, uv).rgb;
    float fogFactor = exp2(-_WaterFogDensity * depthDifference);

    return lerp(_WaterFogColor, backgroundColor, fogFactor * _AlphaScale);
}

unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染

4.实现水面波动

调整好水下折射效果后,我们仍然觉得水面的效果明明如此的波涛汹涌,但是水平面仍然明镜止水般镇定,显然不太符合常识。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染
最后在前面的渲染基础上,我们在顶点着色器内对flowmap进行采样,求解其向量长度,并用于太高vertex的位置(y方向)。

当然,这里使用的是100x100的plane,此外,在顶点着色器无法使用tex2D进行贴图采样,我们需要使用tex2Dlod来采样lowmap,并提供四位浮点数的uv。

我们给uv的第三,第四位置为0。

unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染

v2f vert (appdata_tan v)
{
    v2f o;
    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    float2 flowVec;
    flowVec = tex2Dlod(_FlowMap, float4(o.uv + _Time.y * _Speed, 0.0, 0.0)).rg;
    flowVec = flowVec * 2 -1;

    o.vertex = UnityObjectToClipPos(v.vertex + float3(0.0, length(flowVec) * _HeightScale, 0.0));
    o.screenpos = ComputeScreenPos(o.vertex);

    float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    float3 worldTangent = UnityObjectToWorldDir(v.tangent);
    float3 worldBiTangent = cross(worldNormal, worldTangent) * v.tangent.w;

    o.t2w_0 = float4(worldTangent.x,worldBiTangent.x,worldNormal.x, worldPos.x);
    o.t2w_1 = float4(worldTangent.y,worldBiTangent.y,worldNormal.y, worldPos.y);
    o.t2w_2 = float4(worldTangent.z,worldBiTangent.z,worldNormal.z, worldPos.z);
    
    return o;
}

这样水体与浸没水体的物体边缘的交互会随着时间产生一定波动,能够显得更加真实一些。
unity湖面shader,unity之路,unity,游戏引擎,游戏,材质,着色器,技术美术,图形渲染文章来源地址https://www.toymoban.com/news/detail-839835.html

到了这里,关于【unity shader】水体渲染基础-水下透视效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity Shader ASE基础效果思路与代码(一):遮罩、硬边溶解、光边溶解、UV扰动

    效果展示: 思路与代码 :主纹理和遮罩纹理相乘,其中,两个纹理给到UV流动 步骤与详解 :panner节点:平移 效果展示: 思路与代码 :原图和噪声贴图的透明通道混合,改变噪声贴图的透明度即可 步骤与详解 : 在SubShader中关掉深度写入,混合模式为Alpha Blend,Tags设为透明

    2024年02月22日
    浏览(39)
  • 【Unity-Shader脚本】0基础学会通过用Unity-Shader脚本渲染图像数据(NV21,NV12,RGBA数据)详细教程--附demo,NV21测试图像,YUV图像查看器。

      最近有一个需求是需要我在Unity中将获取到的图像数据来展示在Unity的界面之中。功能其实很简单,熟悉Unity-Shader的小伙伴可能很快就可以做出来。然而我很少和图像的渲染打交道,基本上是0基础了,在做这个需求之前连Shader是什么都不知道。本文记录了自己做这个需求

    2024年02月03日
    浏览(43)
  • 【Unity Shader】Unity前向渲染

    ForwardBase Pass(优先渲染),渲染一个逐像素平行光和所有的顶点/球面调和光,阴影只和平行光有关系,那阴影应该是这个Pass中实现的 ForwardAdd Pass(需要和Base配合使用,否则不生效),渲染剩余全部逐像素灯光 Unity会根据场景中各个光源的设置以及这些光源对物体的影响程

    2024年02月08日
    浏览(47)
  • Unity中Shader编译目标渲染器

    Unity中Shader编译到目标渲染器 #pragma only_renderers 仅编译指定平台的Shader d3d11 - Direct3D 11/12 glcore - OpenGL 3.x/4.x gles - OpenGL ES 2.0 gles3 - OpenGL ES 3.x metal - iOS/Mac Metal vulkan - Vulkan d3d11_9x - Direct3D 11 9.x功能级别,通常在WSA平台上使用 xboxone - Xbox One ps4 - PlayStation 4 psp2 - PlayStation Vita n3ds -

    2024年02月05日
    浏览(37)
  • Unity shader 入门之渲染管线一、总览

     如下示意图 应用阶段(ApplicationStage):准备场景信息(视景体,摄像机参数)、粗粒度剔除、定义每个模型的渲染命令(材质,shader)——由开发者定义,不做讨论。 几何阶段(GemetryStage):顶点着色器、曲面细分着色器、几何着色器、裁剪、屏幕映射; 光栅化阶段(Rasterizer

    2024年02月11日
    浏览(32)
  • 【Unity大气渲染】Unity Shader中实现大气散射(半成品)

    写在前面 这是之前在做天空盒的时候同步写的分析博客,结果后面写到一半就忘了继续了,这里先贴出当时写的半成品,有小伙伴问我怎么做的,这里只能尽力把之前的半成品先放出来了(写得很乱,勿怪orz),,后面有机会会完善好的!希望能帮到大家~ 前置知识学习 【

    2024年02月08日
    浏览(35)
  • unity build-in 渲染管线升级urp渲染 shader篇

            由于工作原因需要对项目进行升级,从build-in渲染管线升级到urp渲染管线,我自己对应的unity版本是2018.版本升级到2021.3.2版本,由于最近几年unity版本升级比较快,个体版本差异有所不同,如遇与版本不一致问题敬请谅解。以下是根据官网等系列网站整理的内容

    2023年04月16日
    浏览(44)
  • Unity大面积草地渲染——1、Shader控制一棵草的渲染

    目录 1、Shader控制一棵草的渲染 2、草地的动态交互 3、使用GPUInstancing渲染大面积的草 4、对大面积草地进行区域剔除和显示等级设置 大家好,我是阿赵。 这里开始讲大面积草地渲染的第一个部分,一棵草的渲染。按照惯例,完整shader在最后。前面是原理的介绍。 这里我自己

    2024年02月12日
    浏览(36)
  • Unity Shader从内置渲染管线迁移到URP

    Unity 在URP中将shader更新为了HLSL语言,使用build-in shader 无法直接在URP中使用 这里讲一下关于shader的更新方法 参考 From Built-in to URP Tags 添加 \\\"RenderPipeline\\\" = \\\"UniversalPipeline\\\" CGPROGRAM ENDCG 改变为 HLSLPROGRAM ENDHLSL #include \\\"UnityCG.cginc\\\" 更改为 #include \\\"Packages/com.unity.render-pipelines.universal/Sh

    2024年02月05日
    浏览(32)
  • Unity Shader学习3:透明效果

    Unity中的透明效果由透明通道控制(RGBA中的A),其值为0是完全透明,为1时完全不透明。有两种方法可以实现透明效果: 透明度测试(Alpha Test) 和 透明度混合(Alpha Blend) 。 透明度测试是指通过特定的条件(通常是Alpha通道的值是否超过某个阈值)来判断片元是否透明,只

    2024年01月19日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包