belnder中VAT的实现

顶点动画纹理在blender中的实现

顶点动画是一个很高效地实现动画手段,现在说下如何在blender中使用插件,在unity实现顶点动画。
blender中的插件代码

bl_info = {
    "name": "VAT_Tool",
    "author": "Wang",
    "version": (0, 1),
    "blender": (3, 1, 0),
    "location": "",
    "description": "VAT tool",
    "warning": "",
    "doc_url": "",
    "category": "Generic",
}

import copy
import json
import os
from itertools import chain

import bmesh
import bpy
from bpy.types import AddonPreferences, Operator, Panel, PropertyGroup


class Anim_property(bpy.types.PropertyGroup):
    name:bpy.props.StringProperty(default="name",description='name')
    is_recode:bpy.props.BoolProperty(default=False,description='is need recoded')
    

class VAT_property(bpy.types.PropertyGroup):
    go_name:bpy.props.StringProperty(default="Armature",description='最上层父物体的名字')
    mesh_name:bpy.props.StringProperty(default="elf",description='mesh物体的名字')


    x_min:bpy.props.FloatProperty(default=-4,description ="min x")
    x_max:bpy.props.FloatProperty(default=4,description ="max x")
    y_min:bpy.props.FloatProperty(default=-4,description ="min y")
    y_max:bpy.props.FloatProperty(default=4,description ="max y")
    z_min:bpy.props.FloatProperty(default=-4,description ="min z")
    z_max:bpy.props.FloatProperty(default=4,description ="max z")

    size:bpy.props.IntProperty(default=512,description ="vat size")
    out_dir:bpy.props.StringProperty(default="C:\\Users\\vert_anim_out",description='输出文件夹')
 
 
class OT_VAT__Bake(Operator):
    bl_idname = "vat.vat_bake"
    bl_label = "bake vertex animation"
    bl_options = {'REGISTER', 'UNDO'}

    def save_image_as(self ,image: bpy.types.Image, path: str=bpy.app.tempdir, file_format: str='PNG', color: str='RGBA', color_depth: str='16', compression: int=0):
        scene = bpy.data.scenes.new("Temp")
        # use docs.blender.org/api/current/bpy.types.ImageFormatSettings.html for more properties
        settings = scene.render.image_settings
        settings.file_format = file_format  # Options: 'BMP', 'IRIS', 'PNG', 'JPEG', 'JPEG2000', 'TARGA', 'TARGA_RAW', 'CINEON', 'DPX', 'OPEN_EXR_MULTILAYER', 'OPEN_EXR', 'HDR', 'TIFF', 'WEBP'
        settings.color_mode = color  # Options: 'BW', 'RGB', 'RGBA' (depends on file_format)
        settings.color_depth = color_depth  # Options: '8', '10', '12', '16', '32' (depends on file_format)
        settings.compression = compression  # Range: 0 - 100
        image.save_render(path, scene)
        bpy.data.scenes.remove(scene)

    def execute(self, context):

        scene = context.scene
        bpy.context.scene.render.image_settings.file_format = "PNG"  # Options: 'BMP', 'IRIS', 'PNG', 'JPEG', 'JPEG2000', 'TARGA', 'TARGA_RAW', 'CINEON', 'DPX', 'OPEN_EXR_MULTILAYER', 'OPEN_EXR', 'HDR', 'TIFF', 'WEBP'
        bpy.context.scene.render.image_settings.color_mode = 'RGBA'  # Options: 'BW', 'RGB', 'RGBA' (depends on file_format)
        bpy.context.scene.render.image_settings.color_depth = '16'  # Options: '8', '10', '12', '16', '32' (depends on file_format)
        bpy.context.scene.render.image_settings.compression = 0  # Range: 0 - 100
                    
        vat_property = scene.vat_property
        
        anims_settings = scene.anims_settings
        anims_settings_map = {}
        for item in anims_settings:
            anims_settings_map[item.name] = item.is_recode
            
        x_range = vat_property.x_max - vat_property.x_min
        y_range = vat_property.y_max - vat_property.y_min
        z_range = vat_property.z_max - vat_property.z_min

        max_verts_count = vat_property.size
        self.left_count = max_verts_count
        armature = bpy.context.scene.objects[vat_property.go_name]
        #ob = bpy.context.active_object
        ob = bpy.context.scene.objects[vat_property.mesh_name]
        me = ob.data
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.uv_texture_add() 

        bm = bmesh.from_edit_mesh(me)

        uv_layer = bm.loops.layers.uv.verify()

        #t = ob.modifiers["Armature"]


        #t.use_vertex_groups = False
        # t.ratio =(1.0*max_verts_count)/verts_len
        
        # bbbb = []
        # for i in range(len(me.vertices)):
        #     bbbb.append(copy.deepcopy(me.vertices[i].co))

        # t.use_vertex_groups = True
        # me = ob.data

        # bpy.ops.object.mode_set(mode='OBJECT')
        # t = ob.modifiers.new(name="Remesh", type='DECIMATE')
        # t.ratio =(1.0*max_verts_count)/verts_len

        # bpy.ops.object.mode_set(mode='EDIT')
        # bm = bmesh.from_edit_mesh(me)

        #bpy.ops.object.mode_set(mode='OBJECT')

        file_dir = vat_property.out_dir
        if not os.path.exists(file_dir) :
            os.makedirs(file_dir)
        
        
        # adjust uv coordinates
        for face in bm.faces:
            for loop in face.loops:
                loop_uv = loop[uv_layer]
                # use xy position of the vertex as a uv coordinate
                loop_uv.uv.x = loop.vert.index/max_verts_count + (1.0/(2*max_verts_count))
                loop_uv.uv.y = 0.5
        bmesh.update_edit_mesh(me) 

        

        verts_count = len(bm.verts)
        print(verts_count)

        acts_frame_count = {}
        acts_name = {}
        acts_recode_info = {}
        json_info = {}
        json_info['size'] = max_verts_count
        json_info['info'] = acts_recode_info

        for act in bpy.data.actions:
            
            frame_count = act.frame_range[1] - act.frame_range[0] + 1.0
            frame_count = frame_count
            if frame_count > max_verts_count :
                continue
            #name = act.name.split('|')[-1]
            name = act.name
            print(name)
            acts_name[name] = act.name
            acts_frame_count[name] = frame_count

        acts_sort = []
        for name in acts_frame_count:
            index = len(acts_sort)
            for i in range(0, len(acts_sort)):
                if acts_frame_count[acts_sort[i]] < acts_frame_count[name] :
                    index = i
                    break
            acts_sort.insert(index ,name)

        acts_is_recode = []
        for i in range(0 ,len(acts_sort)):
            is_recode = anims_settings_map[acts_sort[i]]
            if is_recode:
                acts_is_recode.append(False)
            else:
                acts_is_recode.append(True)
    

        img_index = 0
        def isCompleted():
            for is_recode in acts_is_recode:
                if not is_recode :
                    return False
            return True

        bpy.ops.image.new

        
        while not isCompleted():
            oldImage = bpy.data.images.get("POS"+str(img_index), None)
            oldNormalImage = bpy.data.images.get("NORMAL"+str(img_index), None)
            if oldImage:
                bpy.data.images.remove(oldImage)
            if oldNormalImage:
                bpy.data.images.remove(oldNormalImage)
            
            newImage = bpy.data.images.new("POS"+str(img_index), max_verts_count, max_verts_count, alpha=True)
            newNormalImage = bpy.data.images.new("NORMAL"+str(img_index), max_verts_count, max_verts_count, alpha=True)

            left_start_index = 0
            self.left_count = max_verts_count
            img_pixels = []
            img_normals = []

            def bake_vat_img(self ,i):
                print(acts_name[ acts_sort[i] ])
                action = bpy.data.actions[acts_name[ acts_sort[i] ]]

                #bpy.context.object.animation_data.action = action
                armature.animation_data.action = action
                acts_is_recode[i] = True
                start = int(action.frame_range[0])
                end = int(action.frame_range[1])
                bpy.context.scene.frame_start = start
                bpy.context.scene.frame_end = end
                
                cur_frame_count = end - start + 1
                
                self.left_count = self.left_count - cur_frame_count

                acts_recode_info[acts_sort[i]] = [max_verts_count - (self.left_count + cur_frame_count) ,cur_frame_count]

                print("left_count : " + str(self.left_count))
                print([x_range, y_range, z_range])

                for x in range(start ,end + 1) :
                    bpy.context.scene.frame_set(x)
                    ob.update_from_editmode()
                    depsgraph = bpy.context.evaluated_depsgraph_get()
                    ob_eval = ob.evaluated_get(depsgraph)
                    me = ob_eval.to_mesh()
                    for a in range(max_verts_count):
                        if a >=  verts_count :
                            img_pixels.extend([0.0, 0.0, 0.0, 0.0])
                            img_normals.extend([0.0, 0.0, 0.0, 0.0])
                        else :
                            red = ((me.vertices[a].co.x)-vat_property.x_min) / x_range
                            green = ((me.vertices[a].co.y)-vat_property.y_min) / y_range
                            blue = ((me.vertices[a].co.z) - vat_property.z_min) / z_range
                            alpha = 1.0
                            img_pixels.extend([red, green, blue, alpha])

                            red = (me.vertices[a].normal.x)*0.5 +0.5
                            green = (me.vertices[a].normal.y)*0.5 +0.5
                            blue = (me.vertices[a].normal.z)*0.5 +0.5
                            alpha = 1.0
                            img_normals.extend([red, green, blue, alpha])
                            
                    ob_eval.to_mesh_clear()
            def recode(self):
                for i in range(0 ,len(acts_sort)):
                    if not acts_is_recode[i] and acts_frame_count[acts_sort[i]] < self.left_count :
                        bake_vat_img(self ,i)
                        return True
                return False
            
            while recode(self):
                print("------")

            newImage.pixels = img_pixels
            newImage.update()
            # newImage.file_format = 'PNG'
            # newImage.filepath_raw = file_dir + '\\POS_'+str(img_index) + ".PNG"
            # newImage.save()
            newImage.save_render( file_dir + '\\POS'+str(img_index) + ".PNG",scene=bpy.context.scene)
            #self.save_image_as(newImage ,file_dir + '\\POS_'+str(img_index) + ".PNG")

            newNormalImage.pixels = img_normals
            newNormalImage.update()
            newNormalImage.save_render( file_dir + '\\NORMAL'+str(img_index) + ".PNG",scene=bpy.context.scene)
            # newNormalImage.file_format = 'PNG'
            # newNormalImage.filepath_raw = file_dir + '\\NORMAL_'+str(img_index) + ".PNG"
            # newNormalImage.save()
            #self.save_image_as(newNormalImage ,file_dir + '\\NORMAL_'+str(img_index) + ".PNG")

            img_index = img_index + 1
        
        f = open(file_dir + '\\anim_info.txt', 'w', encoding='utf-8')
        f.write(json.dumps(json_info))
        f.close()
        print("----------------FINISHED")

        bpy.ops.object.mode_set(mode='OBJECT')
        return {'FINISHED'}
    
class OT_VAT_Anim(Operator):
    bl_idname = "vat.vat_anim"
    bl_label = "select vertex animation"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        scene = context.scene
        anims_settings = scene.anims_settings
        for act in bpy.data.actions:
            item = anims_settings.add()
            item.name = act.name
            item.is_recode = True
        return {'FINISHED'}
        

class VAT_PT_Panel(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label="VAT Panel"
    bl_idname="VAT_PT_Panel"
    bl_space_type="VIEW_3D"
    bl_region_type="UI"
    bl_category="VAT_Tool"
    

    def draw(self, context):
        layout = self.layout
        scene = context.scene
        vat_property = scene.vat_property
        anims_settings = scene.anims_settings
        
        layout.label(text="VAT 工具")
        row = layout.row()
        row.scale_y = 3.0
         
        layout.label(text="物体的name")
        layout.prop(vat_property,'go_name',text="name")

        layout.label(text="mesh物体的name")
        layout.prop(vat_property,'mesh_name',text="name")
        
        layout.label(text="VAT的size")
        layout.prop(vat_property,'size',text="size")
        
        layout.label(text="选择动画")
        row = layout.row()
        row.operator("vat.vat_anim")
        
        for item in anims_settings :
            layout.prop(item,'is_recode',text=item.name)

        layout.label(text="X坐标范围")
        row = layout.row()
        row.prop(vat_property, "x_min" ,text = "min X")
        row.prop(vat_property, "x_max" ,text = "max X")

        layout.label(text="y坐标范围")
        row = layout.row()
        row.prop(vat_property, "y_min" ,text = "min Y")
        row.prop(vat_property, "y_max" ,text = "max Y")

        layout.label(text="z坐标范围")
        row = layout.row()
        row.prop(vat_property, "z_min" ,text = "min Z")
        row.prop(vat_property, "z_max" ,text = "max Z")

        layout.label(text="输出文件夹")
        layout.prop(vat_property,'out_dir',text="dir")

        layout.label(text="开始生成VAT")
        row = layout.row()
        row.scale_y = 3.0
        row.operator("vat.vat_bake")


def register():
    bpy.utils.register_class(VAT_property)
    bpy.utils.register_class(Anim_property)
    bpy.types.Scene.vat_property = bpy.props.PointerProperty(type=VAT_property)
    bpy.types.Scene.anims_settings = bpy.props.CollectionProperty(type=Anim_property)
    bpy.utils.register_class(OT_VAT__Bake)
    bpy.utils.register_class(OT_VAT_Anim)
    bpy.utils.register_class(VAT_PT_Panel)


def unregister():
    bpy.utils.unregister_class(OT_VAT__Bake)
    bpy.utils.unregister_class(OT_VAT_Anim)
    bpy.utils.unregister_class(VAT_PT_Panel)

if __name__ == "__main__":
    register()

在这里插入图片描述
点击启用后,会出现面板。
在这里插入图片描述

其中name表示选中物体的名字。mesh name表示mesh的名字,然后勾选要烘培的动画名称。

点击bake按钮。会在输出文件夹生成如下文件:
在这里插入图片描述
这里是unity中的C#代码:

using System.Collections.Generic;
using System.IO;
using LitJson;
using UnityEditor;
using UnityEngine;

namespace Game.VAT
{
    [CreateAssetMenu(fileName ="VAT_Info",menuName ="ScriptableObject/NewTab",order = 1 )]
    public class VAT_Info : ScriptableObject
    {
        public List<string> AnimNames;
        
        public List<Vector2> AnimInfos;

        public int VAT_Size;

        public int RunAnimIndex;
        public int AtkAnimIndex;
        public int DeathAnimIndex;
        
    }
    
    //++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++编辑器代码
#if UNITY_EDITOR
    [CustomEditor(typeof(VAT_Info))]
    public class VertsAnimationMonoBehaviourEditor : Editor
    {
        VAT_Info vertsAnimation;

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();
            
            if (GUILayout.Button("载入动画json信息", GUILayout.Width(200f)))
            {
                vertsAnimation = (VAT_Info) target;
                if (vertsAnimation.AnimInfos != null)
                {
                    vertsAnimation.AnimInfos.Clear();
                }
                else
                {
                    vertsAnimation.AnimInfos = new List<Vector2>(); 
                }

                if (vertsAnimation.AnimNames != null)
                {
                    vertsAnimation.AnimNames.Clear();
                }
                else
                {
                    vertsAnimation.AnimNames = new List<string>();
                }
                string jsonPath = EditorUtility.OpenFilePanel("载入动画json信息", Application.dataPath, "txt");
                string jsonStr= File.ReadAllText(jsonPath);
                JsonData jsonObject = JsonMapper.ToObject(jsonStr);
                vertsAnimation.VAT_Size = (int)jsonObject["size"];
                JsonData info = jsonObject["info"];
                foreach(string key in info.Keys)
                {
                    Vector2 startIndex_count = Vector2.zero;
                    startIndex_count.x = (int) info[key][0] + 0.5f;
                    startIndex_count.y = (int) info[key][1] - 1.0f;
                    vertsAnimation.AnimNames.Add(key);
                    vertsAnimation.AnimInfos.Add(startIndex_count);
                }
                EditorUtility.SetDirty(vertsAnimation);
                
            }
        }
    }
#endif
//------------------------------------
//------------------------------------
}

创建一个vat的scriptableobject资产,
在这里插入图片描述
在这里插入图片描述
点击载入json信息,选择之前生成的json文件。
在这里插入图片描述
附上shader代码:

Shader "Unlit/VertAnimCharacter"
{
    Properties
    {
        _BaseMap ("Base Texture",2D) = "white"{}
        _BaseColor("Base Color",Color)=(1,1,1,1)
        _AnimMap ("VAT 位置图",2D) = "black"{}
        _NormalMap("VAT 法线图",2D) = "black"{}
        _PlayPos("播放位置" ,Range(0.01,1)) = 0.1
        _RimLightWidth("_OutLineWidth" ,Range(0,1)) = 0.2
        [Toggle]_IsSpecular("是否开启高光", Float) = 1
        [Toggle(_IS_OPEN_SHADOW_ON)]_IS_OPEN_SHADOW ("_IS_OPEN_SHADOW", Float) = 0
        [Toggle(_IS_ADDLIGHTS_ON)]_IS_ADDLIGHTS ("_IS_ADDLIGHTS", Float) = 0
        [Toggle(_IS_RimLight_ON)]_IS_RimLight ("_IS_RimLight_ON", Float) = 0
    }
    SubShader
    {
        Tags
        {
            "RenderPipeline"="UniversalPipeline"
            "Queue"="Geometry"
            "RenderType"="Opaque"
        }
        HLSLINCLUDE
        #include "../Common/Common.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
        #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
       
        CBUFFER_START(UnityPerMaterial)
        float4 _BaseMap_ST;
        float4 _AnimMap_ST;
        half4 _BaseColor;
        half _IsSpecular;
        half _RimLightWidth;
        CBUFFER_END
        UNITY_INSTANCING_BUFFER_START(Props)
             UNITY_DEFINE_INSTANCED_PROP(float, _PlayPos)
        UNITY_INSTANCING_BUFFER_END(Props)
        
        TEXTURE2D(_BaseMap);
        SAMPLER(sampler_BaseMap);
        
        TEXTURE2D(_AnimMap);
        SAMPLER(sampler_AnimMap);
       
        ENDHLSL

        Pass
        {
            Tags{"LightMode"="UniversalForward"}
            Cull Front

            HLSLPROGRAM //CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _ _IS_OPEN_SHADOW_ON
            #pragma multi_compile _ _IS_ADDLIGHTS_ON
            #pragma multi_compile _ _IS_RimLight_ON
            TEXTURE2D(_NormalMap);

            struct Attributes
            {
                float4 positionOS : POSITION;
                float4 normalOS : NORMAL;
                float2 uv : TEXCOORD;
                float2 uv2 : TEXCOORD1;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };
            struct Varings//这就是v2f
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD;
                float3 normalWS : NORMAL;
                float3 positionWS : TEXCOORD1;
                float3 viewDirWS : TEXCOORD2;
#ifdef _IS_RimLight_ON                
                float3 normalVS : TEXCOORD3;
                float4 scrPos : TEXCOORD4;
#endif                
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };
            
            float CorrectDepth(float rawDepth)
            {
                float persp = LinearEyeDepth(rawDepth ,_ZBufferParams);
                float ortho = (_ProjectionParams.z-_ProjectionParams.y)*(1-rawDepth)+_ProjectionParams.y;
                return lerp(persp,ortho,unity_OrthoParams.w);
            }

            Varings vert(Attributes IN)
            {
                Varings OUT;
                UNITY_SETUP_INSTANCE_ID(IN);
                UNITY_TRANSFER_INSTANCE_ID(IN, OUT);
                float playPos =UNITY_ACCESS_INSTANCED_PROP(Props, _PlayPos);
                //half3 h = tex2Dlod(_AnimMap, float4(IN.uv2,1,1)).xyz;
                half3 h = SAMPLE_TEXTURE2D_LOD(_AnimMap, sampler_AnimMap ,float2(IN.uv2.x ,playPos) ,0.0).rgb;
                //half3 h = half3(1.0,1.0,1.0);
                h = h*half3(4,4,4) - half3(2,2,0);
                
                half3 n = SAMPLE_TEXTURE2D_LOD(_NormalMap, sampler_AnimMap ,float2(IN.uv2.x ,playPos) ,0.0).rgb;
                
                n = 2 * (n - half3(0.5,0.5,0.5));
                //h = h*half3(1,-1,1);
                //h = IN.positionOS.xyz + h;
                //VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz + h);
                VertexPositionInputs positionInputs = GetVertexPositionInputs(h);
                VertexNormalInputs normalInputs = GetVertexNormalInputs(n);
                OUT.positionCS = positionInputs.positionCS;
                OUT.positionWS = positionInputs.positionWS;
                //OUT.positionCS = TransformWorldToHClip(OUT.positionWS);
                OUT.uv=TRANSFORM_TEX(IN.uv,_BaseMap);
                OUT.viewDirWS = GetCameraPositionWS() - positionInputs.positionWS;
                OUT.normalWS = normalInputs.normalWS;
#ifdef _IS_RimLight_ON                  
                OUT.normalVS = normalize(mul(normalInputs.normalWS, UNITY_MATRIX_IT_MV));
                OUT.scrPos = ComputeScreenPos(positionInputs.positionCS);
#endif                
                return OUT;
            }
            

            half4 frag(Varings IN):SV_Target
            {   
#ifdef _IS_RimLight_ON                   
                float3 nVS = normalize(IN.normalVS);
                float d = SampleSceneDepth(IN.scrPos.xy/IN.scrPos.w + nVS.xy * 5 * _RimLightWidth / _ScreenParams.xy);
                d = CorrectDepth(d);
#endif                
           
                UNITY_SETUP_INSTANCE_ID(IN);
                half4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
                //half3 ddx_positionWS = ddx(IN.positionWS);
                //half3 ddy_positionWS = ddy(IN.positionWS);
#ifdef _IS_OPEN_SHADOW_ON                 
                float4 SHADOW_COORDS = TransformWorldToShadowCoord(IN.positionWS);
                Light light = GetMainLight(SHADOW_COORDS);
#else
                Light light = GetMainLight();
#endif                
                half3 n = normalize(IN.normalWS);
                //half3 n = normalize(-cross(ddx_positionWS ,ddy_positionWS));
                half3 v = normalize(IN.viewDirWS);
                half3 h = normalize(light.direction + v);
                
                
                half nl = max(0.0,dot(light.direction ,n));
                half nh = max(0.0,dot(h ,n));
#ifdef _IS_OPEN_SHADOW_ON                  
                half atten = step(0.5, light.shadowAttenuation);
#else
                half atten = 1.0;
#endif  
                half3 diffuse = lerp(0.2 ,1,atten) * lerp(0.2*baseMap.xyz ,baseMap.xyz ,nl);
                half3 specular = _IsSpecular * atten * light.color * step(0.9,pow(nh ,8));
                
#ifdef _IS_ADDLIGHTS_ON                
                uint pixelLightCount = GetAdditionalLightsCount();
                for (uint lightIndex = 0; lightIndex < pixelLightCount; ++lightIndex)
                {
                    Light add_light = GetAdditionalLight(lightIndex, IN.positionWS);
                    half3 add_h = normalize(add_light.direction + v);
                
                
                    half add_nl = max(0.0,dot(add_light.direction ,n));
                    half add_nh = max(0.0,dot(add_h ,n));
                    diffuse += baseMap.xyz * add_nl* add_light.color * add_light.distanceAttenuation;
                    specular += _IsSpecular * add_light.color * add_light.distanceAttenuation * step(0.9,pow(add_nh ,8));
                }
#endif
                half3 color = diffuse*_BaseColor.xyz;
#ifdef _IS_RimLight_ON                 
    #if UNITY_REVERSED_Z
                    float aa = saturate(d - CorrectDepth(IN.scrPos.z/IN.scrPos.w));  
    #else
                    float aa = saturate(d - CorrectDepth(IN.scrPos.z/IN.scrPos.w));  
    #endif
                half4 final = lerp( half4(color ,1.0) ,half4(1,0,0,1) ,aa );
#else
                half4 final = half4(color ,1.0);     
#endif
                
                return final;
            }
            ENDHLSL  //ENDCG          
        }
        
        pass {
			Tags{ "LightMode" = "ShadowCaster" }
			
			Cull OFF
			
			HLSLPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_instancing
			#pragma multi_compile _IS_OPEN_SHADOW_ON
 
			struct Attributes
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD;
                float2 uv2 : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};
 
			struct Varings
			{
				float4 pos : SV_POSITION;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			Varings vert(Attributes v)
			{
				Varings o = (Varings)0;
				UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);
                float playPos =UNITY_ACCESS_INSTANCED_PROP(Props, _PlayPos);
                half3 h = SAMPLE_TEXTURE2D_LOD(_AnimMap, sampler_AnimMap ,float2(v.uv2.x ,playPos) ,0.0).rgb;
                h = h*half3(4,4,4) - half3(2,2,0);
                
				o.pos = mul(UNITY_MATRIX_MVP,float4(h ,1.0));
				//o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
				return o;
			}
			float4 frag(Varings i) : SV_Target
			{
			    UNITY_SETUP_INSTANCE_ID(i);
				return half4(0.0,0.0,0.0,1.0);
			}
			ENDHLSL
		}
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值