unity用ComputeShader做模型流体喷涂喷绘工具

news/2024/11/8 12:14:34/

最近在研究喷涂喷绘项目,需要做大量纹理图形运算,因此更适合用GPU来处理,在unity中用ComputeShader完成像素运算,SurfaceShader完成纹理渲染。

实现思路:

1.用射线碰撞模型,得到碰撞纹理坐标brushX和brushY

2.ComputeShader拿到brush坐标,在纹理相应的位置做像素运算,并输出纹理ComputeTexture

3.通过SurfaceShader渲染纹理

管理代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.VersionControl;
using UnityEngine;
using UnityEngine.UI;namespace Game.Patinter
{public class PatintManager : MonoBehaviour{//图片尺寸public static int textureSize = 1024;//渲染模型public GameObject paintSurfacePanel;//渲染模型public GameObject paintDataPanel;//运算shaderprivate ComputeShader computeShader;//运算纹理private RenderTexture computeTexture;//输出纹理private Texture2D resultTexture;private int computeShaderKernel;// Start is called before the first frame updatevoid Start(){//实例化shadercomputeShader = (ComputeShader) Resources.Load("Shader/PaintComputeShader", typeof(ComputeShader));computeShaderKernel = computeShader.FindKernel("PaintCompute");//创建纹理computeTexture = new RenderTexture(textureSize, textureSize, 24);computeTexture.enableRandomWrite = true;computeTexture.Create();//绑定纹理computeShader.SetTexture(computeShaderKernel, "paintTexture", computeTexture);computeShader.SetInt("textureSize", textureSize);//输出纹理resultTexture = new Texture2D(textureSize, textureSize, TextureFormat.ARGB32, false);}// Update is called once per framevoid Update(){//鼠标左键按下开始喷绘if (Input.GetMouseButton(0)){var ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit;Physics.Raycast(ray, out hit, 100);//检测到碰撞体if (hit.collider != null){var hitX = hit.textureCoord.x;var hitY = hit.textureCoord.y;Render(hitX, hitY);return;}//未检测到碰撞体Render(0, 0);}else{Render(0, 0);}//空格清空面板if (Input.GetKeyDown(KeyCode.Space)){computeTexture = new RenderTexture(textureSize, textureSize, 24);computeTexture.enableRandomWrite = true;computeTexture.Create();computeShader.SetTexture(computeShaderKernel, "paintTexture", computeTexture);Render(0, 0);}}void Render(float hitX, float hitY){//传入绘制点computeShader.SetFloat("brushX", hitX * textureSize);computeShader.SetFloat("brushY", hitY * textureSize);//纹理运算computeShader.Dispatch(computeShaderKernel, textureSize / 8, textureSize / 8, 1);//拷贝纹理RenderTexture.active = computeTexture;resultTexture.ReadPixels(new Rect(0, 0, textureSize, textureSize), 0, 0);resultTexture.Apply();//渲染纹理paintDataPanel.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", resultTexture);paintSurfacePanel.GetComponent<MeshRenderer>().material.SetTexture("_PaintDataTex", resultTexture);}}
}

代码很简单,就80行;在Start中初始化;Update中检测射线实时渲染;Render负责传入参数启动运算。

ComputeShader:

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel PaintCompute// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> paintTexture;float brushX;
float brushY;int textureSize;[numthreads(8,8,1)]
void PaintCompute(uint3 id : SV_DispatchThreadID)
{//绘制点有效if (brushX != 0 && brushY != 0){// TODO: insert actual code here!//和绘制点距离float dis = distance(id.xy, float2(brushX, brushY));//距离小于90个像素if (dis < 90){float4 v = paintTexture[id.xy];//添加边缘模糊float rate = 1 - dis / 90;v.x += 0.04 * rate;paintTexture[id.xy] = v;}}//流体效果if (id.y > 0){float4 v = paintTexture[id.xy];//大于0.9向下流动if (v.x > 0.9){v.x -= 0.02f;paintTexture[id.xy] = v;float4 down = paintTexture[id.xy - float2(0, 1)];if (down.x < 0.8f)down.x = 0.8;down += float4(0.0178f, 0, 0, 0);paintTexture[id.xy - float2(0, 1)] = down;}}
}

为了更像喷绘的效果,需要加上边缘模糊和流体效果

模型shader:

Shader "Custom/PaintSurfaceShader"
{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}_PaintDataTex ("PaintDataTex (RGB)", 2D) = "black" {}_Glossiness ("Smoothness", Range(0,1)) = 0.5_Metallic ("Metallic", Range(0,1)) = 0.0}SubShader{Tags{"RenderType"="Opaque"}LOD 200CGPROGRAM// Physically based Standard lighting model, and enable shadows on all light types#pragma surface surf Standard fullforwardshadows// Use shader model 3.0 target, to get nicer looking lighting#pragma target 3.0sampler2D _MainTex;sampler2D _PaintDataTex;struct Input{float2 uv_MainTex;float3 worldRefl;INTERNAL_DATA};half _Glossiness;half _Metallic;fixed4 _Color;// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.// #pragma instancing_options assumeuniformscalingUNITY_INSTANCING_BUFFER_START(Props)// put more per-instance properties hereUNITY_INSTANCING_BUFFER_END(Props)void surf(Input IN, inout SurfaceOutputStandard o){// Albedo comes from a texture tinted by colorfixed4 c = tex2D(_MainTex, IN.uv_MainTex);fixed4 p = tex2D(_PaintDataTex, IN.uv_MainTex);o.Albedo = c.rgb * (1 - p.r) + _Color * p.r;// Metallic and smoothness come from slider variableso.Metallic = _Metallic;o.Smoothness = _Glossiness;o.Alpha = c.a;}ENDCG}FallBack "Diffuse"
}

将模型本身的纹理和喷涂上色混合

热力图Shader:

Shader "Custom/PaintDataShader"
{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "black" {}_ColorMapTex ("ColorMap (RGB)", 2D) = "black" {}_Glossiness ("Smoothness", Range(0,1)) = 0.5_Metallic ("Metallic", Range(0,1)) = 0.0}SubShader{Tags{"RenderType"="Opaque"}LOD 200CGPROGRAM// Physically based Standard lighting model, and enable shadows on all light types#pragma surface surf Standard fullforwardshadows// Use shader model 3.0 target, to get nicer looking lighting#pragma target 3.0sampler2D _MainTex;sampler2D _ColorMapTex;struct Input{float2 uv_MainTex;float3 worldPos;};half _Glossiness;half _Metallic;fixed4 _Color;// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.// #pragma instancing_options assumeuniformscalingUNITY_INSTANCING_BUFFER_START(Props)// put more per-instance properties hereUNITY_INSTANCING_BUFFER_END(Props)void surf(Input IN, inout SurfaceOutputStandard o){// Albedo comes from a texture tinted by colorfixed4 c = tex2D(_MainTex, IN.uv_MainTex);fixed4 m = tex2D(_ColorMapTex, float2((1 - c.r)*0.9 + 0.09, 0.1));o.Albedo = m.rgb;// Metallic and smoothness come from slider variableso.Metallic = _Metallic;o.Smoothness = _Glossiness;o.Alpha = c.a;}ENDCG}FallBack "Diffuse"
}

此功能是为了反映喷涂渲染状况

效果:

 左边是热力图,右边效果图

源码:https://download.csdn.net/download/u014261855/87777737

后记:流体没有+法线,还有优化空间,有兴趣的朋友可以优化讨论


http://www.ppmy.cn/news/67926.html

相关文章

Js中的微任务和宏任务

1.前言 任务可以分成两种&#xff0c;一种是同步任务&#xff08;synchronous&#xff09;&#xff0c;另一种是异步&#xff08;asynchronous&#xff09;&#xff0c;异步任务又分为宏任务和微任务。 同步任务&#xff1a;在主线程上排队执行的任务&#xff0c;只有前一个任…

基于分布鲁棒联合机会约束的能源和储备调度(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …

办理日本TELEC认证需要多久?流程是什么?

日本telec认证一般申请办理流程: 1.准备资料和样品&#xff0c;填写申请表; 2.深圳讯科检测机构实验室初步测试样品和审核资料; 3.机构正式向telec提出申请; 4.telec审查提交的文件: 5.样品测试&#xff0c;可由telec或者授权实验室测试&#xff0c;提供测试报告; 6.测试和文件…

MySQL 数据库 高可用 MAH

概述 什么是 MHA MHA&#xff08;Master High Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。 MHA能在故障切换的过程中最…

Python装饰器

目录 官方定义 函数装饰器的使用 实现一个最简单的装饰器 多装饰器嵌套 带参数的装饰器 装饰器内获取被修改函数的属性 类装饰器的使用 参考资料 官方定义 装饰器本质上是一个Python函数&#xff0c;它可以让被装饰函数或方法在不需要做任何代码变动的前提下增加额外功…

table标签-移动端适配

封装一个组件&#xff0c;该组件需要根据不同设备屏幕宽度自适应调整展示方式。对于 PC 端&#xff0c;以类似 el-table 的形式展示数据&#xff0c;而移动端则以一个类似 item 的形式展示每行数据。 可以先在组件中判断设备类型&#xff0c;如以下示例代码所示&#xff1a; …

MySQL视图与联集

一、VIEW&#xff08;视图&#xff09; 1、 概念 可以被当作是虚拟表或存储查询 视图跟表格的不同是&#xff0c;表格中有实际储存资料&#xff0c;而视图是建立在表格之上的一个架构&#xff0c;它本身并不实际储存资料。 临时表在用户退出或同数据库的连接断开后就自动消…

QT是什么软件

QT是一款跨平台的C图形用户界面(GUI)应用程序开发框架&#xff0c;由Digia公司开发并维护。QT提供了一套易于使用的API&#xff0c;使得开发者可以轻松地开发出高质量的应用程序&#xff0c;这些应用程序可以运行在各种操作系统和设备上&#xff0c;包括Windows、Mac OS X、Lin…