0
\$\begingroup\$

I created a character model in Blender with non-spherical eyes. The only way I can make the eye movement look good is if I move the pupil Image Textures along the eye meshes.

The way I've achieved the pupil movement in Blender is with modifiers, which Unity apparently doesn't work with. I've tried "UV Warp" controlled by bone and "UV Project" controlled by bone, and moving the eye bones in Unity does nothing.

So, I'm kind of stumped and desperate for something that will work for my use case. Any ideas that will be Unity-friendly, please?

\$\endgroup\$
1
  • \$\begingroup\$ Sounds like you should show us examples of the geometry you're dealing with, the UV unwrap, and the kind of motion you get from your current modifiers that you need to emulate in Unity. Keep in mind you can edit your existing question instead of posting a new one about the same issue. \$\endgroup\$
    – DMGregory
    Commented Jun 5, 2020 at 13:07

1 Answer 1

2
\$\begingroup\$

I know this might be late a late responce to your question, but I happened to have made a solution while making something for VRChat. This script moves textures based on changing their UV offset, which might be different from what you are looking for. Although it is a great alternative.

I recently made a script that works by moving the textures on a shader applied to a material. It works with three different textures, and using a bone rotation from 0-2 (1 is default) on the x and y axis, it moves the texture up and down and side to side. You will need to know how to write in C# to modify the script for your needs. This script was made originally made to move textures on a surface to replicate the movement of an eye. Here is the shader code that the materials need to have when you put them into the script:

Shader "Custom/MultilayeredShader"
{
    Properties
    {
        _MainTex ("Eye Texture", 2D) = "white" {}
        _SecondTexture("Lower Eyelid", 2D) = "white" {}
        _ThirdTexture("Upper Eyelid", 2D) = "white" {}
        _GlossinessEyelids ("Smoothness Eyelids", Range(0,1)) = 0.5
        _MetallicEyelids ("Metallic Eyelids", Range(0,1)) = 0.0
        _GlossinessEye("Smoothness Eye", Range(0,1)) = 0.5
        _MetallicEye("Metallic Eye", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="transparent" }
        LOD 200
 
        CGPROGRAM
        // 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.0
 
        sampler2D _MainTex;
        sampler2D _SecondTexture;
        sampler2D _ThirdTexture;
        sampler2D _FourthTexture;
 
 
 
        struct Input
        {
            float2 uv_MainTex;
            float2 uv_SecondTexture;
            float2 uv_ThirdTexture;
            float2 uv_FourthTexture;
        };
 
        half _GlossinessEyelids;
        half _MetallicEyelids;
        half _GlossinessEye;
        half _MetallicEye;
        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 assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)
 
        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = (1, 1, 1, 1) + tex2D(_MainTex, IN.uv_MainTex).a * (tex2D(_MainTex, IN.uv_MainTex) - (1, 1, 1, 1));//base white and add eye on top
            c = c + (tex2D(_SecondTexture, IN.uv_SecondTexture).a) * (tex2D(_SecondTexture, IN.uv_SecondTexture) - c);//add lower eyelid on top
            c = c + (tex2D(_ThirdTexture, IN.uv_ThirdTexture).a) * (tex2D(_ThirdTexture, IN.uv_ThirdTexture) - c); // add upper eyelid on top
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = ((tex2D(_SecondTexture, IN.uv_SecondTexture).a+tex2D(_ThirdTexture, IN.uv_ThirdTexture).a) * _MetallicEyelids)  +  ((((tex2D(_SecondTexture, IN.uv_SecondTexture).a + tex2D(_ThirdTexture, IN.uv_ThirdTexture).a)*-1)+1) *  _MetallicEye);
            o.Smoothness = ((tex2D(_SecondTexture, IN.uv_SecondTexture).a + tex2D(_ThirdTexture, IN.uv_ThirdTexture).a) * _GlossinessEyelids) + ((((tex2D(_SecondTexture, IN.uv_SecondTexture).a + tex2D(_ThirdTexture, IN.uv_ThirdTexture).a)*-1) + 1) *  _GlossinessEye);;
            o.Alpha = c.a;
        }
        ENDCG
        //float lerp(float v0, float v1, float t) {
        //  return 
        //}
    }
    FallBack "Diffuse"
}

Here is the script code (It takes the bones, and eye materials with the shader applied as inputs):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class EyeDriver : MonoBehaviour
{
    // Start is called before the first frame update
    
    public Material eyeL;
    public Transform eyeLbone;
    public Transform upperLeft;
    public Transform lowerLeft;
    public Transform eyeRbone;
    public Transform upperRight;
    public Transform lowerRight;
    public Material eyeR;
 
        
    // Use this for initialization
    void Start()
    {
        
    }
 
    // Update is called once per frame
    void Update()
    {
        //left eye
        float y;
        float x;
        if (eyeLbone != null || eyeL != null)
        {
            //left eye code
            x = ((eyeLbone.localRotation.eulerAngles.x - 1)*-1);//turn the 0 to 2 to -1 to 1. Then inverse it.
            y = ((eyeLbone.localRotation.eulerAngles.y - 1)) / 20; //turn the 0-2 to -1 to 1. then make it move slower up and down.
            if ((eyeLbone.localRotation.eulerAngles.x - 1) > 0) //if it is negative.. (going towards center of face)
            {
                
                x = (x / 8);
                eyeL.SetTextureOffset("_MainTex", new Vector2(x, y)); //make it move slower towards the center of the face
            }
            else
            {
                x = (x / 4);
                eyeL.SetTextureOffset("_MainTex", new Vector2(x, y)); //else make it move normal speed aka 1/4 speed.
            }
        }
        //right eye
        if(eyeRbone != null || eyeR != null)
        {
            x = (eyeRbone.localRotation.eulerAngles.x - 1);
            y = (eyeRbone.localRotation.eulerAngles.y - 1) / 20;
            if ((eyeRbone.localRotation.eulerAngles.x - 1) > 0)
            {
                eyeR.SetTextureOffset("_MainTex", new Vector2((x / 4), y));
            }
            else
            {
                eyeR.SetTextureOffset("_MainTex", new Vector2((x / 8), y));
            }
        }
        //upperright
        if (upperRight != null || eyeR != null)
        {
            Transform bone = upperRight;
            string texture = "_ThirdTexture";
 
            y = ((0.0175f*((bone.localRotation.eulerAngles.y - 2) * (bone.localRotation.eulerAngles.y - 2)))-0.05f);
            if ((bone.localRotation.eulerAngles.y - 1) > 0)
            {
                eyeR.SetTextureOffset(texture, new Vector2(0, y));
            }
            else
            {
                eyeR.SetTextureOffset(texture, new Vector2(0, y));
            }
        }
        //loweright
        if (lowerRight != null || eyeR != null)
        {
            Transform bone = lowerRight;
            string texture = "_SecondTexture";
 
            y = ((0.0175f * ((bone.localRotation.eulerAngles.y - 2) * (bone.localRotation.eulerAngles.y - 2))) - 0.05f);
            if ((bone.localRotation.eulerAngles.y - 1) > 0)
            {
                eyeR.SetTextureOffset(texture, new Vector2(0, y));
            }
            else
            {
                eyeR.SetTextureOffset(texture, new Vector2(0, y));
            }
        }
        //upperleft
        if (upperLeft != null || eyeL != null)
        {
            Transform bone = upperLeft;
            string texture = "_ThirdTexture";
 
            y = ((0.0175f * ((bone.localRotation.eulerAngles.y - 2) * (bone.localRotation.eulerAngles.y - 2))) - 0.05f);
            if ((bone.localRotation.eulerAngles.y - 1) > 0)
            {
                eyeL.SetTextureOffset(texture, new Vector2(0, y));
            }
            else
            {
                eyeL.SetTextureOffset(texture, new Vector2(0, y));
            }
        }
        //lowerleft
        if (lowerLeft != null || eyeL != null)
        {
            Transform bone = lowerLeft;
            string texture = "_SecondTexture"; 
 
            y = ((0.0175f * ((bone.localRotation.eulerAngles.y - 2) * (bone.localRotation.eulerAngles.y - 2))) - 0.05f);
            if ((bone.localRotation.eulerAngles.y - 1) > 0)
            {
                eyeL.SetTextureOffset(texture, new Vector2(0, y));
            }
            else
            {
                eyeL.SetTextureOffset(texture, new Vector2(0, y));
            }
        }
    }
}

The script part of this code is just a proof of concept, the math in the code will need to be changed to fit your needs.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Welcome & thanks for contributing to GDSE. Please edit to either include your code directly in your answer or provide enough information to recreate similar code based on your explanation. Doing so ensures your contribution is useful even if the linked material becomes unavailable. \$\endgroup\$
    – Pikalek
    Commented Dec 1, 2020 at 5:06
  • 1
    \$\begingroup\$ Thanks Pikalek! I changed it to have a few comments and I added the code into the answer. \$\endgroup\$
    – 989onan
    Commented Dec 2, 2020 at 15:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .