處理雲陰影,打算直接在post階段做個貼花了事
根據世界坐標的xz做為紋理采樣的坐標,加上旋轉縮放,形成運行的雲陰影
采樣器要用clamp方式
於是這樣聲明了一個
SAMPLER(sampler_CloudTex)
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Clamp;//no work
AddressV = Clamp;//no work
};
發現不啟作用
去官網看了一下,發現有內置的采樣器定義
“Point”, “Linear” or “Trilinear” (required) set up texture filtering mode.
“Clamp”, “Repeat”, “Mirror” or “MirrorOnce” (required) set up texture wrap mode.Wrap modes can be specified per-axis (UVW), e.g. “ClampU_RepeatV”.
“Compare” (optional) set up sampler for depth comparison; use with HLSL SamplerComparisonState type and SampleCmp / SampleCmpLevelZero functions.
意思是根據名字組合,如
sampler_<過濾><UV處理> 形成一個變量名,用這個變量作為參數形成采樣
例如:sampler_LinearClamp 這個表示采樣過濾是linear,超過(0,1)用clamp方式采樣
SAMPLER(sampler_LinearClamp);在shader里這樣聲明變量
采樣是這樣使用:
float4 cloud = SAMPLE_TEXTURE2D_X(_CloudTex, sampler_LinearClamp, cloud_uv);
也可以這樣
_CloudTex.Sample(sampler_LinearClamp, cloud_uv);
對應代碼截圖
unity的有些內置處理真是暗黑。
運行結果如下:
附上代碼,同時也演示了如何在urp添加自定義管線
shader代碼:
Shader "lsc/test_post_fog"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_CloudTex("Texture", 2D) = "white" {}
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
sampler2D _MainTex;
//sampler2D _CameraDepthTexture;
float4x4 _mtx_view_inv;
float4x4 _mtx_proj_inv;
//float4x4 _mtx_clip_to_world;
TEXTURE2D_X_FLOAT(_CameraDepthTexture);
SAMPLER(sampler_CameraDepthTexture);
//cloud
TEXTURE2D_X_FLOAT(_CloudTex);
SAMPLER(sampler_LinearClamp);//work
float4x4 _mtx_cloud_uv_rotate;
float4 _cloud_offset_scale;
//SAMPLER(sampler_CloudTex)
//{
// Filter = MIN_MAG_MIP_LINEAR;
// AddressU = Clamp;//no work
// AddressV = Clamp;//no work
//};
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 screen_pos : TEXCOORD1;
float2 ndc_pos : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
//o.vertex = UnityObjectToClipPos(v.vertex);
VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
o.vertex = vertexInput.positionCS;
o.uv = v.uv;
o.screen_pos = ComputeScreenPos(o.vertex);
o.ndc_pos = (v.uv) * 2.0 - 1.0;
return o;
}
float4 frag(v2f i) : SV_Target
{
float4 view_pos;
float3 world_pos;
float depth01 = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, i.uv).r;
//lsc 取出線性深度,即攝影機空間的z坐標
float linearDepthZ = LinearEyeDepth(depth01, _ZBufferParams);
//lsc 紋理映射轉換到標准空間
float4 screen_pos = float4(i.ndc_pos.x, i.ndc_pos.y, depth01, 1);
//lsc 轉成齊次坐標
screen_pos = screen_pos * linearDepthZ;
//lsc 還原攝影機空間坐標
view_pos = mul(_mtx_proj_inv, screen_pos);
//lsc 世界
world_pos = mul(_mtx_view_inv, float4(view_pos.xyz, 1));
//高度霧衰減
float h_percent = saturate(((world_pos.y - 0.0f) / 20.0f));
float fac_h = exp(-h_percent * h_percent);
//距離霧衰減
float dis = length(world_pos.xyz - _WorldSpaceCameraPos.xyz);
float d_percent = 1 - ((dis - 50.0) / 100.0f);
d_percent = saturate(d_percent);
float fac_d = exp(-d_percent * d_percent);
float4 final_col;
final_col.w = 1.0;
float4 col = tex2D(_MainTex, i.uv);
//cloud 處理雲陰影,紋理平移旋轉
float2 cloud_uv = (world_pos.xz - _cloud_offset_scale.xy) / _cloud_offset_scale.z;
cloud_uv = cloud_uv * 2 - 1;
cloud_uv = mul(_mtx_cloud_uv_rotate, float4(cloud_uv.x, 0, cloud_uv.y, 0)).xz;
cloud_uv = cloud_uv * 0.5 + 0.5;
float4 cloud = SAMPLE_TEXTURE2D_X(_CloudTex, sampler_LinearClamp, cloud_uv);// _CloudTex.Sample(sampler_LinearClamp, cloud_uv);
float cloud_dis = length(world_pos.xz - (_cloud_offset_scale.xy + _cloud_offset_scale.z/2));
cloud.r = cloud.r * pow(clamp(1 - cloud_dis / _cloud_offset_scale.z * 2, 0, 1), 1);
col.xyz = lerp(col.xyz, float3(0, 0, 0), cloud.r);
return col;
//最終霧效
final_col.rgb = lerp(col.rgb, float3(0.2, 0.5, 0.8), fac_d * fac_h * 1);
return final_col;
}
ENDHLSL
}
}
}
對應的feature與pass cs代碼
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class FogCustomRenderPassFeature : ScriptableRendererFeature
{
class FogCustomRenderPass : ScriptableRenderPass
{
public RenderTargetIdentifier render_target_color;
public RenderTargetHandle temp_render_target;
public Material custom_full_screen_material = null;
public Texture tex_cloud = null;
// This method is called before executing the render pass.
// It can be used to configure render targets and their clear state. Also to create temporary render target textures.
// When empty this render pass will render to the active camera render target.
// You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
// The render pipeline will ensure target setup and clearing happens in a performant manner.
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
}
// Here you can implement the rendering logic.
// Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
// https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
// You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
//Debug.Log("Execute fog pass");
if (!custom_full_screen_material)
return;
{
Camera cam = renderingData.cameraData.camera;
var mtx_view_inv = cam.worldToCameraMatrix.inverse;
var mtx_proj_inv = cam.projectionMatrix.inverse;
custom_full_screen_material.SetMatrix("_mtx_view_inv", mtx_view_inv);
custom_full_screen_material.SetMatrix("_mtx_proj_inv", mtx_proj_inv);
custom_full_screen_material.SetTexture("_CloudTex", tex_cloud);
Vector4 cloud_offset_scale = new Vector4(350, 350, 100, 1);
custom_full_screen_material.SetVector("_cloud_offset_scale", cloud_offset_scale);
Matrix4x4 cloud_rotate = Matrix4x4.identity;
//cloud_rotate = Matrix4x4.Rotate(Quaternion.AxisAngle(new Vector3(0, 1, 0),9 * Time.time));
cloud_rotate = Matrix4x4.Rotate(Quaternion.Euler(0, 30 * Time.time, 0));
custom_full_screen_material.SetMatrix("_mtx_cloud_uv_rotate", cloud_rotate);
//Debug.Log("screen height:" + cam.scaledPixelHeight + " screen width:" + cam.scaledPixelWidth);
//Debug.Log("pass Execute view_inv:" + mtx_view_inv + " proj_inv:" + mtx_proj_inv);
}
const string CommandBufferTag = "FogCustomRenderPassFeature Pass";
var cmd = CommandBufferPool.Get(CommandBufferTag);
RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
opaqueDesc.depthBufferBits = 0;
cmd.GetTemporaryRT(temp_render_target.id, opaqueDesc);
// 通過材質,將計算結果存入臨時緩沖區
cmd.Blit(render_target_color, temp_render_target.Identifier(), custom_full_screen_material);
// 再從臨時緩沖區存入主紋理
cmd.Blit(temp_render_target.Identifier(), render_target_color);
// 執行命令緩沖區
context.ExecuteCommandBuffer(cmd);
// 釋放命令緩存
CommandBufferPool.Release(cmd);
// 釋放臨時RT
cmd.ReleaseTemporaryRT(temp_render_target.id);
}
// Cleanup any allocated resources that were created during the execution of this render pass.
public override void OnCameraCleanup(CommandBuffer cmd)
{
}
}
FogCustomRenderPass m_ScriptablePass;
public Material custom_full_screen_material = null;
public Texture tex_cloud = null;
/// <inheritdoc/>
public override void Create()
{
//Debug.Log("Create fog pass");
m_ScriptablePass = new FogCustomRenderPass();
// Configures where the render pass should be injected.
m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
//m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
}
// Here you can inject one or multiple render passes in the renderer.
// This method is called when setting up the renderer once per-camera.
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
m_ScriptablePass.render_target_color = renderer.cameraColorTarget;
m_ScriptablePass.custom_full_screen_material = custom_full_screen_material;
m_ScriptablePass.tex_cloud = tex_cloud;
renderer.EnqueuePass(m_ScriptablePass);
}
}