下面和大家分享一個shader案例,如何實作角色動态地面印記。效果如下圖所示:角色在地形上行走,地形上始終會有一個動态圓環跟随角色,類似于陰影的一個效果。但是這個
“陰影”是我們可以自己控制它的效果的,如圖2所示是通過修改後得到的效果圖:圓環是有一定頻率别的收縮,這種效果在遊戲中我們經常能看到。
角色動态地面印記實作方法
1、簡單圓環版本
實作一的效果的Shader為
Shader "Custom/RadiusShader" {
Properties {
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Center("Center", Vector) = (0,0,0,0)
_Radius("Radius", Float) = 0.5
_RadiusColor("Radius Color", Color) = (1,0,0,1)
_RadiusWidth("Radius Width", Float) = 2
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
fixed4 _Color;
float3 _Center;
float _Radius;
fixed4 _RadiusColor;
float _RadiusWidth;
struct Input {
float2 uv_MainTex;
float3 worldPos;
};
void surf(Input IN, inout SurfaceOutputStandard o) {
float d = distance(_Center, IN.worldPos);
if (d > _Radius && d < _Radius + _RadiusWidth)
o.Albedo = _RadiusColor;
else
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
FallBack "Diffuse"
}
主要的思路其實就是在顔色輸出的時候判斷目前覺得位置一定半徑距離範圍,然後将這個範圍内或者邊上的顔色處理成我們預設的,代碼中_Center 就是角色的世界坐标系的位置,這個位置通過C#代碼在Update裡給這個Shader傳值,并且将半徑和寬度值也
float d = distance(_Center, IN.worldPos);
if (d > _Radius && d < _Radius + _RadiusWidth)
o.Albedo = _RadiusColor;
else
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
一起傳給Shader,代碼如:
using UnityEngine;
[ExecuteInEditMode]
public class Radius : MonoBehaviour {
public Material radiusMaterial;
public float radius = 1;
public Color color = Color.white;
// Use this for initialization
void Start () {
}
//每幀更新shader中的參數值
// Update is called once per frame
void Update () {
radiusMaterial.SetVector("_Center", transform.position);
radiusMaterial.SetFloat("_Radius", radius);
radiusMaterial.SetColor("_RadiusColor", color);
}
}
這種效果比較簡單,有了這個基礎我們再拓展就比較友善了,我自己實驗了介紹裡的第二種效果。
2、周期頻率變化版本
其實核心的部分是類似的,在這無非就是對圓環的半徑寬度做了一個随着時間的正弦函數的變化,并且将預設的顔色從圓心出做了
float4 frag(vertexOutput input) : COLOR
{
float d = distance(_Center,input.worldPos.xyz);
float tempRadiusWidth = _RadiusWidth*abs(sin(_Time.y*_RadiusSpeed));
float4 texCol = tex2D(_MainTex, input.tex.xy);
float4 col = texCol;
if (d > _Radius&&d < _Radius + tempRadiusWidth)
{
col.rgb = texCol.rgb + _RadiusColor*pow(d / (_Radius + tempRadiusWidth), _RadiusPower);
}
else
{
col.rgb = texCol.rgb;
}
return col;
}
漸變的處理,這樣得到的效果會更加融合自然,最後在加上關照計算的處理就可以在實際的應用中使用了。完整的Shader代碼如:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
Shader "Unlit/RPGGroundRender"
{
Properties{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Center("Center", Vector) = (0,0,0,0)
_Radius("Radius",Float) = 0.5
_RadiusColor("Radius Color",Color) = (1,0,0,1)
_RadiusWidth("Radius Width",Float) = 2
_RadiusPower("RadiusPower",Range(0,10)) = 2
_RadiusSpeed("RadiusSpeed",Range(0,10)) = 50
}
SubShader
{
Tags { "RenderType" = "Opaque" "PreviewType"="Plane" }
LOD 200
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
//光源的顔色,Unity的Lighting.cginc檔案中包含這個
float4 _LightColor0;
sampler2D _MainTex;
float3 _Center;
float _Radius;
fixed4 _RadiusColor;
float _RadiusWidth;
float _RadiusPower;
float _RadiusSpeed;
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tex:TEXCOORD0;
};
struct vertexOutput {
float4 pos:SV_POSITION;
float4 worldPos : TEXCOORD1;
float4 col : COLOR;
float4 tex:TEXCOORD0;
};
//頂點程式部分增加了計算光照的部分
vertexOutput vert(vertexInput input)
{
vertexOutput output;
float4x4 modelMatrix = unity_ObjectToWorld;
float4x4 modelMatrixInverse = unity_WorldToObject;
float3 normalDirection = normalize(
mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
float3 diffuseReflection = _LightColor0.rgb * max(0.0, dot(normalDirection, lightDirection));
output.col = float4(diffuseReflection, 1.0);
output.pos= UnityObjectToClipPos(input.vertex);
output.worldPos = mul(unity_ObjectToWorld, input.vertex);
output.tex = input.tex;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
float d = distance(_Center,input.worldPos.xyz);
float tempRadiusWidth = _RadiusWidth*abs(sin(_Time.y*_RadiusSpeed));
float4 texCol = tex2D(_MainTex, input.tex.xy);
float4 col = texCol;
if (d > _Radius&&d < _Radius + tempRadiusWidth)
{
col.rgb = texCol.rgb + _RadiusColor*pow(d / (_Radius + tempRadiusWidth), _RadiusPower);
}
else
{
col.rgb = texCol.rgb;
}
return col;
}
ENDCG
}
}
}
給Shader實時傳資料的C#控制腳本代碼:
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class RPGRenderControl : MonoBehaviour {
public Material radiusMaterial;
public Color color = Color.white;
public float radius = 10f;
public float radiusWidth = 2f;
public float power = 5f;
public float speed = 60f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
//保持渲染位置時刻都跟蹤這個對象
void Update () {
radiusMaterial.SetVector("_Center", transform.position);
radiusMaterial.SetColor("_RadiusColor", color);
radiusMaterial.SetFloat("_Radius", radius);
radiusMaterial.SetFloat("_RadiusWidth", radiusWidth);
radiusMaterial.SetFloat("_RadiusPower", power);
radiusMaterial.SetFloat("_RadiusSpeed", speed);
}
}