C for Graphic:纹理uv着色

      最近有个很实用的shader效果要用到我的游戏中,就是在模型网格纹理上画个小圆圈。

      原理也不复杂,我们先把二维坐标系中纹理和uv想象成一张“画布”,就是根据圆心的uv和纹理的宽高去确定圆心像素,然后圆形的半径以及“扩展”和“收缩”的像素宽度去判断“画布”中每个uv像素是否处于圆环上,然后混合着色即可。语言描叙可能不太好理解,画一张图就一目了然,如下:

      

      去判断“画布”中pixel是否处于center为圆心,in和out组成的圆环上即可,shader和c#代码也贴出来,如下:

      

Shader "Custom/ProjectionShader"
{
    Properties
    {
        _MainTex ("Main Texture", 2D) = "white" {}
		_CircleCenter("Circle Center Texcoord",vector) = (0,0,0,0)
		_CircleRadius("Circle Radius Pixel",Range(0,512)) = 100
		_CircleOutWidth("Circle Out Width",Range(0,100)) = 0
		_CircleInWidth("Circle In Width",Range(0,100)) = 0
		_CircleColor("Circle Color",Color) = (1,1,1,1)
		_CircleAlpha("Circle Alpha",Range(0,1)) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			float4 _MainTex_TexelSize;
			float4 _CircleCenter;
			int _CircleRadius;
			int _CircleOutWidth;
			int _CircleInWidth;
			fixed4 _CircleColor;
			float _CircleAlpha;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

			//判断当前uv是否处于center为圆心,radius,out,in组成的圆环上
			//使用点到圆心向量模长判断即可,为了效率无需开方处理
			bool isInoutCircle(float2 center, float2 uv, int radius, int outwidth, int inwidth)
			{
				float2 cpixel = float2(_MainTex_TexelSize.z*center.x, _MainTex_TexelSize.w*center.y);
				float2 fpixel = float2(_MainTex_TexelSize.z*uv.x, _MainTex_TexelSize.w*uv.y);
				float2 f2c = fpixel - cpixel;
				float f2cLenPow2 = f2c.x*f2c.x + f2c.y*f2c.y;
				if (f2cLenPow2 >= (radius - inwidth) * (radius - inwidth) && f2cLenPow2 <= (radius + outwidth)*(radius + outwidth))
				{
					return true;
				}
				return false;
			}

            fixed4 frag (v2f i) : SV_Target
            { 
				fixed4 col = tex2D(_MainTex, i.uv);
				float2 centeruv = float2(_CircleCenter.x, _CircleCenter.y);
				if (isInoutCircle(centeruv,i.uv, _CircleRadius, _CircleOutWidth, _CircleInWidth))
				{
					col = lerp(col, _CircleColor, _CircleAlpha);
				}
                return col;
            }
            ENDCG
        }
    }
}

  

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SphereCircle : MonoBehaviour
{
    private Material cloneMat;

    void Start()
    {
        cloneMat = GetComponent<MeshRenderer>().material;
    }

    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            //使用射线获取当前point的texcoord0
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit) && hit.transform == transform)
            {
#if UNITY_EDITOR
                Debug.DrawLine(ray.origin, hit.point, Color.red);
#endif
                Vector4 uv = new Vector4(hit.textureCoord.x, hit.textureCoord.y);
                cloneMat.SetVector("_CircleCenter", uv);
            }
        }
    }
}

       shader代码无非就是将纹理和uv当做“画布”,然后fragment函数中逐行逐列去判断当前像素点是否处于“圆环”上,然后混合着色即可,c#代码使用raycast去获取当前碰撞点的texcoord0,十分好用的方法。  

       by the way,现在我就不花那么多时间给代码添加过于复杂的注释了,这里默认小伙伴们已经在图形学上学到一定程度了,实际上沿着我的博客从开始看到现在,这些都属于简单的东西,有个idea就能实现的。

       最后看下效果,如下:

       

       so,猫叔登场。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值