【Unity3D】激光灯、碰撞特效

这篇具有很好参考价值的文章主要介绍了【Unity3D】激光灯、碰撞特效。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 需求描述

        本文将模拟激光灯(或碰撞)特效,详细需求如下:

  • 从鼠标位置发射屏幕射线,检测是否与物体发生碰撞
  • 当与物体发生碰撞时,在物体表面覆盖一层激光灯(或碰撞)特效

【Unity3D】激光灯、碰撞特效

        本文代码见→激光灯、碰撞特效

2 原理

        获取屏幕射线与物体的碰撞点,并在 shader 中计算顶点与碰撞点的距离(记为 dist),通过以下衰减函数计算顶点对应的透明度,透明度随碰撞点的距离增大逐渐减小,激光灯(或碰撞)效果逐渐减弱。

alpha = pow(exp(-dist), 4)

        为使特效更加逼真,激光灯(或碰撞)特效的红色分量由以下漫反射公式控制。其中,red 为红色分量值,λ 为漫反射因子,值越大,漫反射效果越强,本文 λ = 0.1;lightDir 为顶点光源向量,normalDir 为顶点法线向量。

red = λ * dot(lightDir, normalDir) + (1 - λ)

3 需求实现

        SelectController.cs

using UnityEngine;
 
public class SelectController : MonoBehaviour { // 单击选中控制
    private Transform target; // 选中的目标
    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; // 当前对象及其子对象的渲染器
    private Material colliderMaterial; // 激光灯(碰撞)材质
    private Vector4 hitPos; // 碰撞点
    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() { // 绑定子网格
        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) { // 绑定子网格
        if (mesh.subMeshCount == 1) {
            return;
        }
        if (mesh.subMeshCount > materialsLength) {
            return;
        }
        mesh.subMeshCount++;
        mesh.SetTriangles(mesh.triangles, mesh.subMeshCount - 1);
    }
}

        ColliderEffect.shader

Shader "MyShader/ColliderEffect" {
    Properties {
        _HitPos ("HitPos", Vector) = (0, 0, 0, 0) // 屏幕射线碰撞位置
        _Enable ("Enable", Int) = 0 // 是否开启特效
    }

    SubShader {
        Tags {
            // 渲染队列: Background(1000, 后台)、Geometry(2000, 几何体, 默认)、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); // 裁剪坐标系下顶点坐标
                o.worldPos = mul(unity_ObjectToWorld, v.vertex); // 世界坐标系下顶点坐标
                o.worldNormal = UnityObjectToWorldNormal(v.normal); // 世界坐标系下顶点法线向量
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                if(_Enable == 1) {
                    float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 世界坐标系下由顶点指向光源的方向向量
                    float diffuse = 0.1 * dot(worldLightDir, i.worldNormal) + 0.9; // 漫反射颜色强度
                    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 运行效果

【Unity3D】激光灯、碰撞特效文章来源地址https://www.toymoban.com/news/detail-424285.html

5 推荐阅读

  •  渲染管线
  • 固定管线着色器一
  • 固定管线着色器二
  • 表面着色器
  • 顶点和片元着色器
  • 选中物体描边特效
  • 基于模板测试和顶点膨胀的描边方法
  • 水波特效
  • 半球卷屏特效
  • 卷轴特效

到了这里,关于【Unity3D】激光灯、碰撞特效的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【Unity3D】高斯模糊特效

    1 高斯模糊原理         边缘检测特效中使用了卷积运算进行了边缘检测,本文实现的高斯模糊特效同样使用了卷积运算,关于卷积核和卷积运算的概念,读者可以参考边缘检测特效。         本文完整资源见→Unity3D高斯模糊特效。         我们将用于模糊处理的卷积

    2024年02月07日
    浏览(46)
  • 【Unity3D】地面网格特效

    1 前言         本文实现了地面网格特效,包含以下两种模式: 实时模式:网格线宽度和间距随相机的高度实时变化; 分段模式:将相机高度分段,网格线宽度和间距在每段中对应一个值。         本文完整资源见→Unity3D地面网格特效。  2 地面网格实现          Sc

    2024年02月16日
    浏览(40)
  • Unity3D教程:2D游戏技能特效

    在我们的2D图形游戏中不可缺少大量的光影、技能特效,像Diablo II中的魔法效果的实现,幸好我们拥有强大的CPU来为我们实现Alpha混合与色彩饱和混合,接下来让我们来讨论一下如何用这些方法来实现我们游戏中所需要的技能特效。     一、Alpha混合特效     Alpha混合可以让我

    2024年02月01日
    浏览(61)
  • Unity3d 物体不动,碰撞触发不了的情况

    问题: 有一种情况,主角带刚体,主角站着不动。玩家站在陷阱上,陷阱的碰撞体 Toggle 之后, OnCollisionEnter 触发不了。 解决:盲猜玩家组件上才有刚体,而碰撞检测是刚体运动的时候,才进行检测的。

    2024年02月06日
    浏览(64)
  • Unity3D粒子系统之制作烟雾特效

    本文将会介绍如何使用Unity内的粒子系统制作烟雾效果。 如果想了解Unity粒子系统中的基础属性,可以看这篇博客:Unity3D 粒子系统之基础属性介绍 先附上预览图: 材质贴图 首先我们需要一张烟雾材质用的材质贴图,我是自己画的,可以参考下图自己画一张或者去网上找素材

    2024年02月02日
    浏览(54)
  • Unity3D粒子系统之制作火焰特效

    本文将会介绍如何使用Unity内的粒子系统制作烟雾效果。 如果想了解Unity粒子系统中的基础属性,可以看这篇博客:Unity3D粒子系统之基础属性介绍 先上预览图吧 用自己熟悉的绘画工具画一张类似这样的图片(相似即可,白灰色部分边缘不规则),注意一定要黑底。 将画好的

    2024年02月02日
    浏览(47)
  • Unity3d bounds包围盒 和collider碰撞器区别

    Bounds 叫作外包围盒、边界框、外扩矩形. 是struct 结构体。 而我们获得Bounds的主要途径有三种:Render,Collider,Mesh。 Render.bounds 世界坐标 Collider.bounds 世界坐标 Mesh.bounds  本地坐标 碰撞器的方框始终跟着模型旋转移动,缩放跟着模型的,只要模型不缩放它也不缩放 Bounds 跟随模型移

    2024年02月02日
    浏览(77)
  • 【Unity3D赛车游戏优化篇】【十】汽车粒子特效和引擎咆哮打造极速漂移

    👨‍💻个人主页 :@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏 :Unity游戏demo – 😶‍🌫️版本: Unity2021 😶‍🌫️适合人群:Unity初学者进阶 😶‍🌫️学习目标:3D赛车游戏的基础制作 😶‍🌫️技能

    2024年02月09日
    浏览(68)
  • unity3D中用Mesh Collider创建的碰撞体与物体模型位置有偏差的解决方法

    1.相信很多小伙伴在使用Mesh Collider时都遇到过下面这个情况: 1.解决方法:在这个对象Alpaca下面新建一个空对象Collider,我们把Mesh Collider添加到这个空对象上面就可以调整碰撞体的位置了  3.完成效果:

    2024年02月13日
    浏览(56)
  • 【Unity3D日常开发】Unity3D中协程的使用

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 大家好,我是佛系工程师 ☆恬静的小魔龙☆ ,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 最近有小伙伴问协程怎么用、怎么写,我也是会用会写,但是原理不是很明白。 学习了一下,总结出

    2024年02月12日
    浏览(59)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包