草庐IT

【Unity3D】选中物体描边特效

little_fat_sheep 2024-01-07 原文

1 前言

        描边的难点在于如何检测和识别边缘,当前实现描边特效的方法主要有以下几种:

        1)基于顶点膨胀的描边方法

        在 SubShader 中开 2 个 Pass 渲染通道,第一个 Pass 通道渲染膨胀的顶点,即将顶点坐标沿着法线方向向外扩展,并使用纯色给扩展后的顶点着色,第二个 Pass 通道渲染原顶点,并覆盖第一个 Pass 通道渲染的内部。

        该方案实现简单,算法效率高,但是对于拐角较大的两个面交界处,会出现描边断裂,并且描边的宽度会受到透视投影影响。

        基于模板测试和顶点膨胀的描边方法 解决了描边断裂和描边宽度受透视影响问题。

        2)基于法线的描边方法

        对于物体的任意一顶点,判断视线向量(该点指向相机的向量)与该点法线向量的夹角(记为 θ)是否近似 90 度,如果近似 90 度,就将该点识别为边缘,并进行边缘着色。实际应用中,通常采用模糊描边着色法,而不是阈值法,即将 sin(θ) 作为该点渲染描边色的强度。

        该方案属于内描边,实现简单,算法效率高,但是物体中间 θ 值近似 90 度的地方也会被描边。

        3)基于深度纹理的描边方法

        对渲染后的屏幕纹理进行二次渲染,根据像素点周围颜色的差异判断是否是物体边缘像素,如果是边缘,需要重新进行边缘着色。判断边缘的具体做法是:对该像素点周围的像素点进行卷积运算,得到该点的梯度(反映该点附件颜色突变的强度),根据梯度阈值判断该点是否是边缘。

        该方案属于内描边,实现有一定难度,算法效率一般,算法依赖梯度阈值,并且受颜色、光照、阴影等影响较大如:地面的影子可能会被描边。

        4)基于法线和深度纹理的描边方法

        通过引入法线纹理,对方案 3) 进行优化。该方案不仅考虑了深度纹理图中任意点的梯度,还考虑了法线纹理图中该点的梯度,通过综合两种梯度和梯度阈值,确定该点是否需要边缘着色。

        该方案属于内描边,效果较好,实现较难,算法依赖梯度阈值。

        5)基于模板纹理模糊膨胀的描边方法

        首先使用纯色对选中的物体进行渲染,得到模板纹理,接着对模板纹理进行模糊处理,使模板颜色往外扩,得到模糊纹理,再根据模板纹理和模糊纹理对所有物体重新渲染,渲染规则:如果该像素点在模板纹理内部,就渲染原色,如果在模板纹理外部,就根据模糊纹理的透明度判断渲染原色还是模糊纹理色。

        该方案属于外描边,效果较好,实现较难,但算法不依赖阈值。

        本文代码资源见→Unity3D选中物体描边特效

2 基本原理

        本文采用基于模板纹理模糊膨胀的描边方法,本节将通过图文详细介绍该算法的原理。

        1)原图

        2)模板纹理

        说明:清屏颜色为 (0, 0, 0, 0),后面会用到 。通过 Graphics.ExecuteCommandBuffer(commandBuffer) 对选中的物体进行渲染,得到模板纹理。

        3)模糊纹理

        说明:通过对模板纹理进行模糊处理, 使模板颜色向外扩展,得到模糊纹理,外扩的部分就是需要描边的部分

        4)合成纹理

         说明:根据模板纹理和模糊纹理对所有物体重新渲染,渲染规则:如果该像素点在模板纹理内部,就渲染原色,如果在模板纹理外部,就根据模糊纹理的透明度判断渲染原色还是模糊纹理色,如下:

// 由于模糊纹理的外部清屏颜色是(0, 0, 0, 0), blur.a=0, 因此模糊纹理的外部也会被渲染为原色
color.rgb = lerp(source.rgb, blur.rgb, blur.a); // lerp(a,b,x)=(1-x)*a+x*b
color.a = source.a;

        5)描边颜色和宽度渐变

         描边颜色由模板颜色决定,通过设置模板颜色随时间变化,实现描边颜色渐变,通过设置模板透明度随时间变化,实现描边在出现和消失,视觉上感觉描边在扩大和缩小。

fixed4 frag(v2f i) : SV_Target // 片段着色器
{
    float t1 = sin(_Time.z); // _Time = float4(t/20, t, t*2, t*3)
	float t2 = cos(_Time.z);
	// 描边颜色随时间变化, 描边透明度随时间变化, 视觉上感觉描边在膨胀和收缩
	return float4(t1 + 1, t2 + 1, 1 - t1, 1 - t2);
}

        渐变模板图如下:

         渐变描边效果如下:

       

        6)缺陷

         如果描边的物体存在重叠,由于所有物体共一个模板纹理,将存在描边消融现象。

        模板纹理如下:

         描边消融如下:

         可以看到,正方体、球体、胶囊体、圆柱体下方及人体都没有描边特效,因为它们在模板纹理的内部,被消融掉了。

3 代码实现

        OutlineEffect.cs

using System;
using UnityEngine;
using UnityEngine.Rendering;

public class OutlineEffect : MonoBehaviour {
    public static Action<CommandBuffer> renderEvent; // 渲染事件
    public float offsetScale = 2; // 模糊处理像素偏移
    public int iterate = 3; // 模糊处理迭代次数
    public float outlineStrength = 3; // 描边强度

    private Material blurMaterial; // 模糊材质
    private Material compositeMaterial; // 合成材质
    private CommandBuffer commandBuffer; // 用于渲染模板纹理
    private RenderTexture stencilTex; // 模板纹理
    private RenderTexture blurTex; // 模糊纹理

    private void Awake() {
        blurMaterial = new Material(Shader.Find("Custom/Outline/Blur"));
        compositeMaterial = new Material(Shader.Find("Custom/Outline/Composite"));
        commandBuffer = new CommandBuffer();
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination) {
        if (renderEvent != null) {
            RenderStencil(); // 渲染模板纹理
            RenderBlur(source.width, source.height); // 渲染模糊纹理
            RenderComposite(source, destination); // 渲染合成纹理
        } else {
            Graphics.Blit(source, destination); // 保持原图
        }
    }

    private void RenderStencil() { // 渲染模板纹理
        stencilTex = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);
        commandBuffer.SetRenderTarget(stencilTex);
        commandBuffer.ClearRenderTarget(true, true, Color.clear); // 设置模板清屏颜色为(0,0,0,0)
        renderEvent.Invoke(commandBuffer);
        Graphics.ExecuteCommandBuffer(commandBuffer);
    }

    private void RenderBlur(int width, int height) { // 对模板纹理进行模糊化
        blurTex = RenderTexture.GetTemporary(width, height, 0);
        RenderTexture temp = RenderTexture.GetTemporary(width, height, 0);
        blurMaterial.SetFloat("_OffsetScale", offsetScale);
        Graphics.Blit(stencilTex, blurTex, blurMaterial);
        for (int i = 0; i < iterate; i ++) {
            Graphics.Blit(blurTex, temp, blurMaterial);
            Graphics.Blit(temp, blurTex, blurMaterial);
        }
        RenderTexture.ReleaseTemporary(temp);
    }

    private void RenderComposite(RenderTexture source, RenderTexture destination) { // 渲染合成纹理
        compositeMaterial.SetTexture("_MainTex", source);
        compositeMaterial.SetTexture("_StencilTex", stencilTex);
        compositeMaterial.SetTexture("_BlurTex", blurTex);
        compositeMaterial.SetFloat("_OutlineStrength", outlineStrength);
        Graphics.Blit(source, destination, compositeMaterial);
        RenderTexture.ReleaseTemporary(stencilTex);
        RenderTexture.ReleaseTemporary(blurTex);
        stencilTex = null;
        blurTex = null;
    }
}

        说明: OnRenderImage 方法是MonoBehaviour的生命周期方法,在所有的渲染完成后由 MonoBehavior 自动调用,该方法依赖相机组件,由于 OnRenderImage 在渲染后调用,因此被称为后处理操作,它是 Unity3D 特效的重要理论分支;Graphics.Blit(source, dest, material) 用于将 source 纹理按照 material 材质重新渲染到 dest;CommandBuffer 携带一系列的渲染命令,依赖相机,用来拓展渲染管线的渲染效果;OutlineEffect 脚本组件必须挂在相机上

        OutlineObject.cs

using UnityEngine;
using UnityEngine.Rendering;

public class OutlineObject : MonoBehaviour {
    private Material stencilMaterial; // 模板材质

    private void Awake() {
        stencilMaterial = new Material(Shader.Find("Custom/Outline/Stencil"));
    }

    private void OnEnable() {
        OutlineEffect.renderEvent += OnRenderEvent;
        // _StartTime用于控制每个选中的对象颜色渐变不同步
        stencilMaterial.SetFloat("_StartTime", Time.timeSinceLevelLoad * 2);
    }

    private void OnDisable() {
        OutlineEffect.renderEvent -= OnRenderEvent;
    }

    private void OnRenderEvent(CommandBuffer commandBuffer) {
        Renderer[] renderers = gameObject.GetComponentsInChildren<Renderer>();
        foreach (Renderer r in renderers) {
            commandBuffer.DrawRenderer(r, stencilMaterial); // 将renderer和material提交到主camera的commandbuffer列表进行渲染
        }
    }
}

        说明:被选中的物体将会添加 OutlineObject 脚本组件,用于渲染选中对象的模板纹理,每个选中对象独立持有 stencilMaterial,互不干扰,描边的渐变相位(由_StartTime控制)可以由选中对象独立控制,这样每个模板的颜色就可以独立控制,从而实现每个选中对象描边各异的效果。

        SelectController.cs

using System.Collections.Generic;
using UnityEngine;

public class SelectController : MonoBehaviour {
    private List<GameObject> targets; // 选中的游戏对象
    private List<GameObject> loseFocus; // 失焦的游戏对象
    private RaycastHit hit; // 碰撞信息

    private void Start() {
        Camera.main.gameObject.AddComponent<OutlineEffect>();
        targets = new List<GameObject>();
        loseFocus = new List<GameObject>();
        hit = new RaycastHit();
    }

    private void Update() {
        if (Input.GetMouseButtonUp(0)) {
            GameObject hitObj = GetHitObj();
            if (hitObj == null) { // 未选中任何物体, 已描边的全部取消描边
                targets.ForEach(obj => loseFocus.Add(obj));
                targets.Clear();
            }
            else if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) {
                if (targets.Contains(hitObj)) { // Ctrl重复选中, 取消描边
                    loseFocus.Add(hitObj);
                    targets.Remove(hitObj);
                } else { // Ctrl追加描边
                    targets.Add(hitObj);
                }
            } else { // 单选描边
                targets.ForEach(obj => loseFocus.Add(obj));
                targets.Clear();
                targets.Add(hitObj);
                loseFocus.Remove(hitObj);
            }
            DrawOutline();
        }
    }

    private void DrawOutline() { // 描边
        targets.ForEach(obj => {
            if (obj.GetComponent<OutlineObject>() == null) {
                obj.AddComponent<OutlineObject>();
            } else {
                obj.GetComponent<OutlineObject>().enabled = true;
            }
        });
        loseFocus.ForEach(obj => {
            if (obj.GetComponent<OutlineObject>() != null) {
                obj.GetComponent<OutlineObject>().enabled = false;
            }
        });
        loseFocus.Clear();
    }

    private GameObject GetHitObj() { // 获取屏幕射线碰撞的物体
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit)) {
            return hit.collider.gameObject;
        }
        return null;
    }
}

        说明:通过单击物体,给选中的物体添加 OutlineObject 脚本组件,再由 OutlineEffect 控制描边。按住 Ctrl 键再单击物体会追加选中,如果重复选中,会取消描边。

        StencilShader.shader

Shader "Custom/Outline/Stencil"
{
	Properties
	{
		_StartTime ("startTime", Float) = 0 // _StartTime用于控制每个选中的对象颜色渐变不同步
	}

	SubShader
	{
		Pass
		{	
			CGPROGRAM // CG语言的开始
			// 编译指令 着色器名称 函数名称
			#pragma vertex vert // 顶点着色器, 每个顶点执行一次
			#pragma fragment frag // 片段着色器, 每个像素执行一次
			#pragma fragmentoption ARB_precision_hint_fastest // fragment使用最低精度, fp16, 提高性能和速度

			// 导入头文件
			#include "UnityCG.cginc"

			float _StartTime;

			struct appdata // 顶点函数输入结构体
			{
				half4 vertex: POSITION; // 顶点坐标
			};

			struct v2f // 顶点函数输出结构体
			{
				float4 pos : SV_POSITION;
			};
			
			v2f vert(appdata v) // 顶点着色器
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target // 片段着色器
			{
				float t1 = sin(_Time.z - _StartTime); // _Time = float4(t/20, t, t*2, t*3)
				float t2 = cos(_Time.z - _StartTime);
				// 描边颜色随时间变化, 描边透明度随时间变化, 视觉上感觉描边在膨胀和收缩
				return float4(t1 + 1, t2 + 1, 1 - t1, 1 - t2);
			}

			ENDCG // CG语言的结束
		}
	}

	FallBack off
}

        说明: StencilShader 用于渲染模板纹理,并且其颜色和透明度随时间变化,实现描边颜色渐变、宽度在膨胀和收缩效果。_StartTime 用于控制时间偏移,由物体被选中的时间决定,每个物体被选中的时间不一样,因此选中物体模板颜色各异,描边颜色也各异。

        BlurShader.shader

Shader "Custom/Outline/Blur"
{
	Properties
	{
		_MainTex ("stencil", 2D) = "" {}
		_OffsetScale ("offsetScale", Range (0.1, 3)) = 2 // 模糊采样偏移
	}
	
	SubShader
	{
		Pass
		{
			ZTest Always
			Cull Off
			ZWrite Off
			Lighting Off
			Fog { Mode Off }
			
			CGPROGRAM // CG语言的开始
			#pragma vertex vert // 顶点着色器, 每个顶点执行一次
			#pragma fragment frag // 片段着色器, 每个像素执行一次
			#pragma fragmentoption ARB_precision_hint_fastest // fragment使用最低精度, fp16, 提高性能和速度
			
			#include "UnityCG.cginc"

			sampler2D _MainTex;
			half _OffsetScale;
			half4 _MainTex_TexelSize; //_MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)

			struct appdata // 顶点函数输入结构体
			{
				half4 vertex: POSITION;
				half2 texcoord: TEXCOORD0;
			};

			struct v2f // 顶点函数输出结构体
			{
				float4 pos : POSITION;
				half2 uv[4] : TEXCOORD0;
			};
			
			v2f vert (appdata v) // 顶点着色器
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				half2 offs = _MainTex_TexelSize.xy * _OffsetScale;
				// uv坐标向四周扩散
				o.uv[0].x = v.texcoord.x - offs.x;
				o.uv[0].y = v.texcoord.y - offs.y;
				o.uv[1].x = v.texcoord.x + offs.x;
				o.uv[1].y = v.texcoord.y - offs.y;
				o.uv[2].x = v.texcoord.x + offs.x;
				o.uv[2].y = v.texcoord.y + offs.y;
				o.uv[3].x = v.texcoord.x - offs.x;
				o.uv[3].y = v.texcoord.y + offs.y;
				return o;
			}

			fixed4 frag(v2f i) : COLOR // 片段着色器
			{
				fixed4 color1 = tex2D(_MainTex, i.uv[0]);
				fixed4 color2 = tex2D(_MainTex, i.uv[1]);
				fixed4 color3 = tex2D(_MainTex, i.uv[2]);
				fixed4 color4 = tex2D(_MainTex, i.uv[3]);
				fixed4 color;
				// max: 2个向量中每个分量都取较大者, 这里通过max函数将模板的边缘向外扩, rgb=stencil.rgb
				color.rgb = max(color1.rgb, color2.rgb);
				color.rgb = max(color.rgb, color3.rgb);
				color.rgb = max(color.rgb, color4.rgb);
				color.a = (color1.a + color2.a + color3.a + color4.a) / 4; // 透明度向外逐渐减小
				return color;
			}
			
			ENDCG // CG语言的结束
		}
	}
	
	Fallback off
}

        说明: BlurShader 用于渲染模糊纹理,通过对模板纹理模糊化处理,实现模板颜色外扩,外扩的部分就是需要描边的部分。 

        CompositeShader.shader

Shader "Custom/Outline/Composite"
{
	Properties
	{
		_MainTex ("source", 2D) = "" {}
		_StencilTex ("stencil", 2D) = "" {}
		_BlurTex ("blur", 2D) = "" {}
		_OutlineStrength ("OutlineStrength", Range(1, 5)) = 3
	}
	
	SubShader
	{
		Pass
		{
			ZTest Always
			Cull Off
			ZWrite Off
			Lighting Off
			Fog { Mode off }
			
			CGPROGRAM // CG语言的开始
			#pragma vertex vert // 顶点着色器, 每个顶点执行一次
			#pragma fragment frag // 片段着色器, 每个像素执行一次
			#pragma fragmentoption ARB_precision_hint_fastest // fragment使用最低精度, fp16, 提高性能和速度
			
			#include "UnityCG.cginc"
		
			sampler2D _MainTex;
			sampler2D _StencilTex;
			sampler2D _BlurTex;
			float _OutlineStrength;
			float4 _MainTex_TexelSize; //_MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)

			struct appdata // 顶点函数输入结构体
			{
				half4 vertex: POSITION;
				half2 texcoord: TEXCOORD0;
			};

			struct v2f // 顶点函数输出结构体
			{
				float4 pos : POSITION;
				half2 uv : TEXCOORD0;
			};
			
			v2f vert (appdata v) // 顶点着色器
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				if (_MainTex_TexelSize.y < 0)
					o.uv.y = 1 - o.uv.y; // 在Direct3D平台下, 如果我们开启了抗锯齿, 则_MainTex_TexelSize.y 会变成负值
				return o;
			}
			
			fixed4 frag(v2f i) : COLOR // 片段着色器
			{
				fixed4 source = tex2D(_MainTex, i.uv);
				fixed4 stencil = tex2D(_StencilTex, i.uv);
				if (any(stencil.rgb))
				{ // 绘制选中物体
					return source;
				}
				else
				{ // 绘制选中物体以外的图像
					fixed4 blur = tex2D(_BlurTex, i.uv);
					fixed4 color;
					color.rgb = lerp(source.rgb, blur.rgb * _OutlineStrength, saturate(blur.a - stencil.a));
					color.a = source.a;
					return color;
				}
			}

			ENDCG // CG语言的结束
		}
	}
	
	Fallback Off
}

        说明: CompositeShader 用于渲染合成纹理,根据模板纹理和模糊纹理对所有物体重新渲染,渲染规则:如果该像素点在模板纹理内部,就渲染原色,如果在模板纹理外部,就根据模糊纹理的透明度判断渲染原色还是模糊纹理色。

4 运行效果

        单击物体选中描边,按住 Ctrl 键单击物体追加选中描边,单击地面或空白地方所有已描边物体取消描边(删除了地面的碰撞体组件)。

5 拓展

        HighlightingSystem 插件也实现了基于模板纹理模糊膨胀的描边方法,插件资源在Unity3D选中物体描边特效的【Assets\Plugins\HighlightingSystem】目录下。

        该插件与本文的区别在于模板纹理的渲染方式不同,本文通过 CommandBuffer 渲染模板纹理,HighlightingSystem 通过第二个相机渲染模板纹理(camera.Render() 方法),具体操作如下:在 HighlightingEffect 脚本组件的 OnPreRender 方法中,通过 Copy 主相机新生成一个相机,并将其 cullingMask 设置为 highlightingLayer(值为7),用于渲染图层为 7  的物体的模板纹理,被添加 HighlightableObject 脚本组件的物体在渲染模板纹理时其图层将会被临时更改为 7,渲染结束后又恢复原图层。

        该插件存在一个缺陷,7 号图层需要预留出来,否则 7 号图层的物体周围将存在一个模糊的灰色边缘。

        HighlightingSystem 插件的使用方法如下:将 HighlightingEffect 脚本组件挂在相机下,给需要描边的物体添加 SpectrumController 脚本组件。运行时,已添加 SpectrumController 脚本组件的物体将会被自动添加 HighlightableObject 脚本组件。

6 推荐阅读

有关【Unity3D】选中物体描边特效的更多相关文章

  1. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  2. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  3. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  4. unity---接入Admob - 2

    目录1.AdmobSDK下载地址2.将下载好的unityPackagesdk导入到unity里​编辑 3.解析依赖到项目中

  5. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  6. [Vuforia]二.3D物体识别 - 2

    之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶

  7. python - Ruby 或 Python 的 3d 游戏引擎? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion是否有适用于这些的3d游戏引擎?

  8. Unity Shader 学习笔记(5)Shader变体、Shader属性定义技巧、自定义材质面板 - 2

    写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c

  9. 三分钟集成 TapTap 防沉迷 SDK(Unity 版) - 2

    三分钟集成Tap防沉迷SDK(Unity版)一、SDK介绍基于国家对上线所有游戏必须增加防沉迷功能的政策下,TapTap推出防沉迷SDK,供游戏开发者进行接入;允许未成年用户在周五、六、日以及法定节假日晚上8:00-9:00进行游戏,防沉谜时间段进入游戏会弹窗进行提示!开发环境要求:Unity2019.4或更高版本iOS10或更高版本Android5.0(APIlevel21)或更高版本🔗Unity集成Demo参考链接🔗UnityTapSDK功能体验APK下载链接二、集成前准备1.创建应用进入开发者后台,按照提示开始创建应用;2.开通服务在使用TDS实名认证和防沉迷服务之前,需要在上面创建的应

  10. 【Unity大气散射】GAMES104:3A中如何实现大气散射 - 2

    写在前面前两天学习并整理的大气散射基础知识:【Unity大气渲染】关于单次大气散射的理论知识,收获了很多,但不得不承认的是,这其实已经是最早的、90年代的非常古老的方法了,后来也出现了一些优化性的计算思路和方法。因此,我打算先不急着跟各种教程在Unity中实现大气散射,而是再花时间来看看最近的游戏是如何去实现大气渲染的:06.游戏中地形大气和云的渲染(下)|GAMES104-现代游戏引擎:从入门到实践接下来就跟着GAMES104讲地形大气和云渲染的部分学习并做简单的记录,涉及到之前没提到的Mie散射也只选择直接截图PPT的方式记录啦!毕竟对于做作品来说,之后实现出来才是重要的~当然,May佬的

随机推荐