1 需求描述
? ? ? ? 本文將模擬激光燈(或碰撞)特效,詳細(xì)需求如下:
- 從鼠標(biāo)位置發(fā)射屏幕射線,檢測是否與物體發(fā)生碰撞
- 當(dāng)與物體發(fā)生碰撞時(shí),在物體表面覆蓋一層激光燈(或碰撞)特效
????????本文代碼見→激光燈、碰撞特效
2 原理
? ? ? ? 獲取屏幕射線與物體的碰撞點(diǎn),并在 shader 中計(jì)算頂點(diǎn)與碰撞點(diǎn)的距離(記為 dist),通過以下衰減函數(shù)計(jì)算頂點(diǎn)對應(yīng)的透明度,透明度隨碰撞點(diǎn)的距離增大逐漸減小,激光燈(或碰撞)效果逐漸減弱。
alpha = pow(exp(-dist), 4)
? ? ? ? 為使特效更加逼真,激光燈(或碰撞)特效的紅色分量由以下漫反射公式控制。其中,red 為紅色分量值,λ 為漫反射因子,值越大,漫反射效果越強(qiáng),本文?λ = 0.1;lightDir 為頂點(diǎn)光源向量,normalDir 為頂點(diǎn)法線向量。
red = λ * dot(lightDir, normalDir) + (1 - λ)
3 需求實(shí)現(xiàn)
????????SelectController.cs
using UnityEngine;
public class SelectController : MonoBehaviour { // 單擊選中控制
private Transform target; // 選中的目標(biāo)
private RaycastHit hit; // 碰撞信息
private void Update() {
if (Input.GetMouseButtonUp(0)) {
Transform temp = GetHitTrans();
if (temp != target) {
DrawEffect(target, temp);
target = temp;
}
}
}
private void DrawEffect(Transform old, Transform now) { // 繪制特效
DrawEffect(old, false);
DrawEffect(now, true);
}
private void DrawEffect(Transform trans, bool enable) { // 繪制特效
if (trans != null) {
foreach(Transform child in trans.transform.GetComponents<Transform>()) {
if (child.GetComponent<ColliderEffect>() == null) {
if (enable) {
child.gameObject.AddComponent<ColliderEffect>();
}
}
else {
child.GetComponent<ColliderEffect>().enabled = enable;
}
}
}
}
private Transform GetHitTrans() { // 獲取屏幕射線碰撞的物體
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) {
return hit.transform;
}
return null;
}
}
????????ColliderEffect.cs
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
[DisallowMultipleComponent]
public class ColliderEffect : MonoBehaviour { // 激光燈(或碰撞)特效
private Renderer[] renderers; // 當(dāng)前對象及其子對象的渲染器
private Material colliderMaterial; // 激光燈(碰撞)材質(zhì)
private Vector4 hitPos; // 碰撞點(diǎn)
private RaycastHit hit; // 碰撞信息
private void Awake() {
renderers = GetComponentsInChildren<Renderer>();
colliderMaterial = new Material(Shader.Find("MyShader/ColliderEffect"));
hitPos = Vector4.zero;
CombineSubmeshes();
}
private void OnEnable() {
hitPos = GetHitPoint();
colliderMaterial.SetVector("_HitPos", hitPos);
foreach (var renderer in renderers) {
List<Material> materials = renderer.sharedMaterials.ToList();
materials.Add(colliderMaterial);
renderer.sharedMaterials = materials.ToArray();
}
}
private void Update() {
hitPos = GetHitPoint();
if (!hitPos.Equals(Vector4.zero)) {
colliderMaterial.SetInt("_Enable", 1);
colliderMaterial.SetVector("_HitPos", hitPos);
} else {
colliderMaterial.SetInt("_Enable", 0);
}
}
private void OnDisable() {
foreach (var renderer in renderers) {
List<Material> materials = renderer.sharedMaterials.ToList();
materials.Remove(colliderMaterial);
renderer.sharedMaterials = materials.ToArray();
}
}
private Vector4 GetHitPoint() {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 1000) && hit.transform == transform) {
return hit.point;
}
return Vector4.zero;
}
private void CombineSubmeshes() { // 綁定子網(wǎng)格
foreach (var meshFilter in GetComponentsInChildren<MeshFilter>()) {
var renderer = meshFilter.GetComponent<Renderer>();
if (renderer != null) {
CombineSubmeshes(meshFilter.sharedMesh, renderer.sharedMaterials.Length);
}
}
foreach (var skinnedMeshRenderer in GetComponentsInChildren<SkinnedMeshRenderer>()) {
CombineSubmeshes(skinnedMeshRenderer.sharedMesh, skinnedMeshRenderer.sharedMaterials.Length);
}
}
private void CombineSubmeshes(Mesh mesh, int materialsLength) { // 綁定子網(wǎng)格
if (mesh.subMeshCount == 1) {
return;
}
if (mesh.subMeshCount > materialsLength) {
return;
}
mesh.subMeshCount++;
mesh.SetTriangles(mesh.triangles, mesh.subMeshCount - 1);
}
}
????????ColliderEffect.shader文章來源:http://www.zghlxwxcb.cn/news/detail-424285.html
Shader "MyShader/ColliderEffect" {
Properties {
_HitPos ("HitPos", Vector) = (0, 0, 0, 0) // 屏幕射線碰撞位置
_Enable ("Enable", Int) = 0 // 是否開啟特效
}
SubShader {
Tags {
// 渲染隊(duì)列: Background(1000, 后臺)、Geometry(2000, 幾何體, 默認(rèn))、Transparent(3000, 透明)、Overlay(4000, 覆蓋)
"Queue" = "Transparent+110"
"RenderType" = "Transparent"
"DisableBatching" = "True"
}
Pass {
Blend SrcAlpha OneMinusSrcAlpha // 混合測試, 與背后的物體顏色混合
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
uniform int _Enable;
uniform float4 _HitPos;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float4 worldPos : TEXCOORD0;
float3 worldNormal : Normal;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 裁剪坐標(biāo)系下頂點(diǎn)坐標(biāo)
o.worldPos = mul(unity_ObjectToWorld, v.vertex); // 世界坐標(biāo)系下頂點(diǎn)坐標(biāo)
o.worldNormal = UnityObjectToWorldNormal(v.normal); // 世界坐標(biāo)系下頂點(diǎn)法線向量
return o;
}
fixed4 frag(v2f i) : SV_Target {
if(_Enable == 1) {
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 世界坐標(biāo)系下由頂點(diǎn)指向光源的方向向量
float diffuse = 0.1 * dot(worldLightDir, i.worldNormal) + 0.9; // 漫反射顏色強(qiáng)度
float dist = distance(i.worldPos, _HitPos);
float alpha = pow(exp(-dist), 4); // 透明度(隨距離衰減)
return float4(diffuse , 0, 0, alpha);
} else {
return float4(0, 0, 0, 0);
}
}
ENDCG
}
}
}
4 運(yùn)行效果
文章來源地址http://www.zghlxwxcb.cn/news/detail-424285.html
5 推薦閱讀
- ?渲染管線
- 固定管線著色器一
- 固定管線著色器二
- 表面著色器
- 頂點(diǎn)和片元著色器
- 選中物體描邊特效
- 基于模板測試和頂點(diǎn)膨脹的描邊方法
- 水波特效
- 半球卷屏特效
- 卷軸特效
到了這里,關(guān)于【Unity3D】激光燈、碰撞特效的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!