目錄
蠻牛原文:http://www.manew.com/thread-143613-1-1.html
製作過程視頻:https://www.bilibili.com/video/BV1Cf4y127ba
源碼資源:https://github.com/AMikeW/BStandShaderResources
蠻牛原文:http://www.manew.com/thread-143613-1-1.html
製作過程視頻:https://www.bilibili.com/video/BV1Cf4y127ba
源碼資源:https://github.com/AMikeW/BStandShaderResources
一、效果圖
二、實戰
1、製作Shader
Shader "Unlit/SaomiaoShader2"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_StrengthFloat("_StrengthFloat", Float) = -0.5
_AlphaDownFloat("_AlphaDownFloat", Range(0,3)) = 0.5
_LerpFloat("_LerpFloat", Range(0,1)) = 0.8
_Angle("_Angle", Float) = 45
_SlerpFloat("_SlerpFloat", Range(1, 80)) = 20
}
SubShader
{
Blend SrcAlpha OneMinusSrcAlpha
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _StrengthFloat;
float _AlphaDownFloat;
float _LerpFloat;
float _Angle;
float _SlerpFloat;
uniform float _FloatArray[256];
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
float3 nor = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col;
//1.UV座標偏移(UV中心點爲(0,0),左下角爲(-0.5,-0.5),右上角爲(0.5, 0.5))
float2 uv = i.uv;
uv.x = uv.x - 0.5;
uv.y = uv.y - 0.5;
//2.根據UV來計算出一個變化區域:一個以UV中心點爲中心,半徑爲0.5的圓形,圓形外到內從0漸變到1
fixed refDis = 1.0 - sqrt(uv.x * uv.x + uv.y * uv.y) * 2;
//3.中心點向周圍發射的向量(歸一化)
fixed2 fragmentDir = normalize(uv.xy);
//4.半圓弧(正X軸方向): 圓弧中心向兩旁的值從1逐漸變爲0,cos正好滿足
fixed rightHalfCircle = clamp(dot(float2(1, 0), fragmentDir.xy), 0, 1);
//5.漸變半圓弧顏色(內到外)
col = lerp(_Color, fixed4(1, 1, 1, 1), refDis * _LerpFloat);
////6_1.增強邊緣效果
//fixed strengthF = pow(refDis, _StrengthFloat);
//col.a = col.a * refDis * rightHalfCircle * strengthF;
//7.衰減半圓弧兩旁,固定衰減25°角後的
fixed tempAngleCos = cos(radians(_Angle)); //25度角的餘弦值 0.906 31
//6_2.增強邊緣效果 + 潤滑邊緣
fixed strengthF = pow(refDis, _StrengthFloat);
col.a = col.a * refDis * pow(rightHalfCircle / max(0.001, tempAngleCos), _SlerpFloat) * strengthF;
//大於_Angle角度區域的像素衰減 (在視野之外的), _Angle其實是一個視野角度的一半.
if (rightHalfCircle < tempAngleCos)
{
col.a *= _AlphaDownFloat;
}
else {
//核心:視野角度內(_Angle, -Angle)範圍內 進行一個遮擋處理 (這裏就是所說的Shader弧度變化在(angle,-angle))
//計算出index
//fragmentDir.y是歸一化後的向量Y值
//因 sqrt(fragmentDir.y^2 + fragmentDir.x^2) = 1
// sin(fragmentDIr與UV正X軸(0.5,0.5)的角度弧度) 爲 (fragmentDir.y / 1) 即fragmentDir.y ,反過來說fragmentDir.y就是sin(角度)
float curRad = asin(fragmentDir.y); //asin 反正弦函數(正弦值)獲取角度對應的弧度.
curRad += radians(_Angle); //偏移到正數(上面的弧度是指(_Angle, Angle)角度的當前片元所在的角度弧度..有點繞
float f = curRad / radians(_Angle * 2); //當前弧度/總弧度 得到一個係數
float index = f * 256; //係數乘上索引最大值 獲取索引
//因爲C#計算出的當index爲0時,應該是照射區域上方,而此時Shader,想象一下是不是0時爲上方。。。
//答案不是,上方index爲0時,curRad是0,在沒有經過偏移時,它位於照射區域最下。所以應該取反索引 (這裏很繞但是要理解,它就是這樣子..)
index = 256 - index;//日 我SB了
float curFloat = _FloatArray[index];
float curFragmentDistance = sqrt(uv.x * uv.x + uv.y * uv.y) * 2; //[0, 0.5]變爲[0,1] 因爲curFloat是[0,1]範圍的
if (curFloat > 0 && curFragmentDistance > curFloat) {
col *= 0;
}
}
return col;
}
ENDCG
}
}
}
2、創建Plane,賦予Shader所在的材質
3、創建C#腳本,掛在Plane物體身上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Saomiao2 : MonoBehaviour
{
public Material mat;
//旋轉速度
public float speed;
//角度(與Shader一樣,它只是視角角度的一半)
public float angle;
float[] arrayFloat;
//射線長度
public float rayLength;
void Start()
{
arrayFloat = new float[256];
}
void Update()
{
transform.Rotate(transform.up, Time.deltaTime * speed);
UpdateRay();
}
void UpdateRay()
{
//角度轉弧度
float rad = Mathf.Deg2Rad * angle;
//每一次射線遞增步長(一個弧度) //視角角度的弧度/256
float step = rad * 2f / 256;
int index = 0;
for (int i = 1; i <= 256; i++)
{
//step * i 是視角範圍內的一個弧度變化 + 自身角度弧度進行旋轉 注意:要減去一半
//step * i + Mathf.Deg2Rad * (transform.eulerAngles.y + 180) 這部分是 [0,angle*2] 度數的弧度變化
//Shader裏是[-angle, angle]的變化
float curRad = step * i + Mathf.Deg2Rad * (transform.eulerAngles.y + 180) - rad;
//根據當前弧度計算出座標
float x = rayLength * Mathf.Cos(-curRad);
float z = rayLength * Mathf.Sin(-curRad);
Vector3 pos = new Vector3(x, 0, z);
//發射射線
Ray ray = new Ray(transform.position, pos);
RaycastHit raycastHit;
if(Physics.Raycast(ray, out raycastHit))
{
arrayFloat[index] = raycastHit.distance * 1.0f / rayLength; //射線碰到物體後拿到的一個射線到物體之間的距離
Debug.DrawLine(transform.position, pos, Color.red);
}
else
{
arrayFloat[index] = -1;
Debug.DrawLine(transform.position, pos);
}
index++;
//此時index爲0時,射線是位於照射範圍的最上,index=255時,射線位於照射範圍的最下..(一定要理解這一點)
//transform.eulerAngles.y是從0遞增,順時針旋轉,然而實際情況我們的射線會逆時針轉,所以在我取反了curRad進行獲取座標..
//此時當index逐漸增大時,是從照射區域最上方漸變到最下方,到Shader我們就通過這個關係計算出index!
}
mat.SetFloatArray("_FloatArray", arrayFloat);
}
}
注意:角度要保持一樣
4、創建3個Cube拉長放到掃描範圍內