首页 > 其他分享 >Unity URP实现漫画板效果

Unity URP实现漫画板效果

时间:2024-12-30 18:19:41浏览次数:1  
标签:return v2 float length drawingBoardVertex v0 Unity URP 漫画

参考:UE用Masked做视差漫画板(新手向)

可以分成两个部分,一块是画框,一块是绘制框内的内容(以下实现都默认所有顶点在同一平面上)。

画框

创建透明unlit材质,计算边框区域并且着色。创建一个脚本(CreateMesh.cs下称CreateMesh)用于创建和控制四边形网格,CreateMesh可以控制的参数有四个顶点的位置,边框粗细,以及边框效果的材质。

边框效果 Graph Shader:

点击查看CalBounding代码
float3 v01 = v1 - v0;
float3 v03 = v3 - v0;
float3 v21 = v1 - v2;
float3 v23 = v3 - v2;

float3 v0p = w_pos - v0;
float3 v2p = w_pos - v2;

float v01p = dot(normalize(v01), v0p);
float v03p = dot(normalize(v03), v0p);
float v21p = dot(normalize(v21), v2p);
float v23p = dot(normalize(v23), v2p);

float length_01 = length(v0p - normalize(v01) * v01p);
float length_03 = length(v0p - normalize(v03) * v03p);
float length_21 = length(v2p - normalize(v21) * v21p);
float length_23 = length(v2p - normalize(v23) * v23p);

distance = min(min(length_01, length_03), min(length_21, length_23));

if(length_01 < m_thickness || length_03 < m_thickness || length_21 < m_thickness || length_23 < m_thickness)
{
    value =  1;
}
else
{
    value =  0;
}

实现后就有了一个能随意控制的边框了。

内容物

两类判断条件,1.是否在边框(正面)之前,2.是否在边框内。也就是说只有在画框后面并且不能透过画框看到的部分才会被剔除掉。这部分用另一个材质实现。

CreateMesh.cs代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class CreateMesh : MonoBehaviour
{
    [SerializeField]
    public Vector3[] BoundVertex; //注意为局部坐标

    Vector4[] BoundVertexVP = new Vector4[4];
    Vector4[] BoundVertexWS = new Vector4[4];

    [SerializeField, Range(0.0f, 1.0f)]
    public float _m_thickness = 0.05f;

    public Shader shader;
    public Material maskMaterial;

    private MeshFilter meshFilter;
    private Mesh mesh;
    private MeshRenderer meshRenderer;

    // Start is called before the first frame update
    void Start()
    {
        mesh = new Mesh();
        meshFilter = GetComponent<MeshFilter>();
        meshFilter.mesh = mesh;

        mesh.vertices = GetVertex();
        mesh.triangles = GetIndex();
        mesh.uv = GetUV(); 

        meshRenderer = GetComponent<MeshRenderer>();
        meshRenderer.material = new Material(shader);
        UpdataMat();
    }

    private void Update()
    {
        mesh.vertices = GetVertex();
        mesh.triangles = GetIndex();
        UpdataMat();
    }

    void UpdataMat()
    {
        meshRenderer.material.SetVector("_v0", BoundVertex[0]);
        meshRenderer.material.SetVector("_v1", BoundVertex[1]);
        meshRenderer.material.SetVector("_v2", BoundVertex[2]);
        meshRenderer.material.SetVector("_v3", BoundVertex[3]);
        meshRenderer.material.SetFloat("_m_thickness", _m_thickness);

        UpdataSpacePosition();
    }

    void UpdataSpacePosition()
    {
        for (int i = 0; i < BoundVertex.Length; i++)
        {
            BoundVertexWS[i] = transform.TransformPoint(BoundVertex[i]);
            BoundVertexVP[i] = Camera.main.WorldToViewportPoint(BoundVertexWS[i]);
        }
        //Debug.Log(BoundVertexVP[0]);
        maskMaterial.SetVectorArray("_drawingBoardVertex_WS", BoundVertexWS);
        maskMaterial.SetVectorArray("_drawingBoardVertex_VP", BoundVertexVP);
    }

    Vector3[] GetVertex()
    {
        return BoundVertex;
    }

    int[] GetIndex()
    {
        return new int[6] { 0, 1, 2, 2, 3, 0 };
    }

    Vector2[] GetUV()
    {
        return new Vector2[]{
            new Vector2(0, 0), // 左下
            new Vector2(0, 1), // 左上
            new Vector2(1, 1), // 右上
            new Vector2(1, 0)  // 右下
        };
    }

    private void OnDrawGizmos()
    {
        for (int i = 0; i < 4; i++)
        {
            Gizmos.color = Color.red;
            Gizmos.DrawSphere(transform.TransformPoint(BoundVertex[i]), 0.02f);
        }
    }
}

VisibleMask材质代码
Shader "Custom/VisibleMask"
{
    Properties {

	}
	SubShader {
		Tags { "RenderType"="Opaque"}
 
		HLSLINCLUDE
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
		ENDHLSL
 
		Pass {
			Cull Off

			HLSLPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
 
			struct Attributes {
				float4 positionOS	: POSITION;
			};
 
			struct Varyings {
				float4 positionCS 	: POSITION;
				float3 positionWS : TEXCOORD;
			};
 
			float4 _drawingBoardVertex_VP[4];
			float4 _drawingBoardVertex_WS[4];

			float float2cross(float2 A, float2 B){
				return A.x * B.y - A.y * B.x;
			}

			bool IsVertexInFront(float3 posWS){
				float3 e1 = _drawingBoardVertex_WS[1] - _drawingBoardVertex_WS[0];
				float3 e2 = _drawingBoardVertex_WS[2] - _drawingBoardVertex_WS[0];

				float3 normalWS = normalize(cross(e1, e2));
				if(dot(posWS - _drawingBoardVertex_WS[0], normalWS) >= 0) return true;
				return false;
			}

			bool CWTriangle(float2 v0, float2 v1, float2 v2){
				float z = (v1.x - v0.x) * (v2.y - v0.y) - (v1.y - v0.y) * (v2.x - v0.x);
				if (z > 0) return false;
				return true;
			}

			bool IsVertexInTriangle(float2 v0, float2 v1, float2 v2, float2 pos){
				if(!CWTriangle(v0, v1, v2)) return false;
				float c1 = float2cross(pos - v0, v1 - v0);
				float c2 = float2cross(pos - v1, v2 - v1);
				float c3 = float2cross(pos - v2, v0 - v2);
				if(c1 * c2 > 0 && c2 * c3 > 0) return true;
				return false;
			}

			bool IsVertexInQuad(float2 posCS){
				if(IsVertexInTriangle(_drawingBoardVertex_VP[0].xy, _drawingBoardVertex_VP[1].xy, _drawingBoardVertex_VP[2].xy, posCS) || 
				IsVertexInTriangle(_drawingBoardVertex_VP[0].xy, _drawingBoardVertex_VP[2].xy, _drawingBoardVertex_VP[3].xy, posCS)){
					return true;
				}
				return false;
			}
 
			Varyings vert(Attributes IN) {
				Varyings OUT;
 
				VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
				OUT.positionCS = positionInputs.positionCS;
				OUT.positionWS = positionInputs.positionWS;
				return OUT;
			}
			float4 frag(Varyings IN) : SV_Target {
				if(!IsVertexInFront(IN.positionWS) && !IsVertexInQuad(float2(IN.positionCS.x / _ScreenParams.x, IN.positionCS.y / _ScreenParams.y))){
					discard;
				}
				return float4(1., 1. ,1., 1.);
			}
			ENDHLSL
		}
	}
}

通用内容物

目前只想到两个办法,一种是修改VisibleMask材质代码效果为设置模板值,这样只用修改内容物材质的模板采样即可实现漫画板效果,缺陷是内容物需要绘制两次,且如果内容物材质有修改绘制区域的操作(如外描边)是会无效的,而且不兼容built-in Shader Graph实现的材质(built-in Shader Graph不提供模板测试的接口,URP可以的fullscreen可以设置模板测试),但胜在非常方便。
另一种是包装成方法,在内容物材质中调用,不过这样需要修改内容物材质的地方就比较多了。
总的来说,前者非常简单但限制非常多性能不够好,后者更符合直觉只是需要改的地方比较多。如果有更好的方法欢迎大佬指出。

其他

透明背景不是那么的好看,这里再实现一下背景的填充以及修复从后面观察的效果。背景填充纹理的实现方法如下。

最终效果:

标签:return,v2,float,length,drawingBoardVertex,v0,Unity,URP,漫画
From: https://www.cnblogs.com/PAdiD/p/18640218

相关文章

  • burp suite 6 (泷羽sec)
    声明学习视频来自B站UP主泷羽sec,如涉及侵泷羽sec权马上删除文章。笔记只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负这节课旨在扩大自己在网络安全方面的知识面,了解网络安全领域的见闻,了解学习哪些知识对于我们渗透......
  • Unity批处理修改prefab内容并保存
    EditorUtility.DisplayProgressBar("ModifyPrefab","Pleasewait...",0);string[]ids=AssetDatabase.FindAssets("t:Prefab",newstring[]{"Assets/Resources/Prefabs"});for(inti=0;i<ids.Length;i++){......
  • WPF笔记13——CommunityToolKit.Mvvm
    1、[ObservableProperty]标记private字段上有[ObservableProperty]标记,CommunityToolkit.Mvvm会自动给它生成一个对应的public属性,并在属性值改变时自动触发属性变更通知。2、[ObservableObject]标记ObservableObject类型实现了实现了INotifyPropertyChanged和INotifyProperty......
  • 最新扣子(Coze)实战案例:小红书爆款小新歪理漫画,批量处理节点的使用详细讲解,手把手教学
    今天通过一个小红书爆款漫画《小新歪理》来为大家讲解Coze中批处理节点的使用。先看生成后的效果:   接下来,话不多说,斜杠君用最简单的方式教给大家。大家可以关注收藏,以免之后找不到,而且也不会错过我后面的教程。网页链接​mp.weixin.qq.com/s/74WlVI7nCBirDQfMEynQJQ?......
  • Marigold:Repurposing Diffusion-Based Image Generators for Monocular Depth Estimat
    目录一、概述二、相关工作1、单目深度估计2、扩散模型3、单目深度估计的扩散模型4、基础模型三、Method四、实验一、概述    Marigold是一个扩散模型和通过微调手段的单目深度估计方法,可以利用预先训练好的StableDiffusion中的视觉知识,来实现更好更通用......
  • 【从零开始入门unity游戏开发之——unity篇01】unity6基础入门开篇——游戏引擎是什么
    文章目录前言**游戏引擎是什么?****游戏引擎对于我们的意义**1、**降低游戏开发的门槛**2、**提升游戏开发效率****以前做游戏****现在做游戏****主流的游戏引擎有哪些?**Unity相比其他游戏引擎的优势?**为什么选择Unity?**Unity游戏市场占比unity发展前景刚发布不久的Unit......
  • 基于 Unity 引擎的 VR/AR 音视频编解码技术总结
    在VR/AR应用开发中,音视频编解码技术是实现沉浸式体验的关键环节之一。通过高效的音视频处理,可以实现实时通信、虚拟会议、在线视频流、沉浸式音频等功能。本文将围绕Unity引擎的VR/AR开发需求,系统总结音视频编解码的技术原理、常用工具、实现方案及优化策略。1.VR/AR......
  • Unity 引擎实现动作游戏技能和战斗功能的实现与优化
    动作游戏的核心在于流畅的技能与战斗系统,这包括打击判定、技能表现、战斗逻辑以及联机对战等多个关键模块。以下从技能系统实现、打击判定、表现优化和联机功能等方面详细总结Unity引擎如何实现和优化动作游戏的战斗功能。1.技能系统的实现动作游戏的技能系统通常涉及技......
  • 【unity】学习制作类银河恶魔城游戏-4-
    制作攻击计数器给全部攻击动作应用帧事件但是理想情况下应该是,短间隔时间内连续点击鼠标才能连击,加入连击计时器编辑代码修补“桶子”解决攻击时移动的问题解决冲刺时攻击的问题解决无方向键输入时原地冲刺的问题解决空中攻击无法掉落的问题继承“inheritance......
  • 【Unity架构插件】Soap 是一款基于 ScriptableObject 模式的 Unity 插件,旨在通过采用
    Soap是一款基于ScriptableObject模式的Unity插件,旨在通过采用一种清晰且模块化的架构模式,简化项目中的数据管理、事件处理和跨模块的通信。Soap提供了一个轻量级、易于扩展和维护的解决方案,帮助开发者在Unity项目中实现更高效、更清晰的代码结构。主要特点Scriptab......