- Home /
The question is answered, right answer was accepted
Moving vertices in shaders
Hi there,
For my project I have to manipulate mesh at runtime, to do this without clearing and refilling the mesh array (which is very expensive) I'm going to do it in a shader, atleast, try to.
I've browsed the web, including unity answers intensely but I just can't find a tutorial or document which tells me step by step how to achieve this.
The tutorial on displaying normal colors was already a great help, but the shader documentation is still a bit confusing for me (someone who just started to delve into Unity shaders).
I have come as far as:
Properties{
_vertPos("VertPos", vector) = (0,0,0,1) //this to initialize
}
then outside the struct i define my float4 vertPos;
I am able to fill in the vector from script and read it by using
set/getVector("_vertPos");
I've added
#pragma surface surf Lambert vertex:vert
to specify my vertex function, and made a function:
float4 vert(float4 v:POSITION) : SV_POSITION
{
}
But now I'm stuck, and I honestly can't find something that simply specifies how to move a specific vertex. I got the function parameters from the unity documentation, but i'm still unclear what this does, or where i have to put the inout, because I think i have to get the appdata_base in there and find and access the vertices, and hopefully manipulate them according to their location. The main thing I want to manipulate is the height.
I am quite aware of matrix structures, it's just the syntax I'm really struggling with
Any help or an alternative way of moving vertices from a mesh would be much appreciated.
Answer by tanoshimi · Sep 26, 2013 at 09:33 AM
The vertex modifier function of a surface shader doesn't just have an float4 POSITION input and return a float4 SV_POSITION output - rather it operates on the entire appdata_full structure, of which "vertex" is a member. Here's an example of a vertex modifier that modifies the "height" (i.e. z value) of vertices:
Shader "Vertex Modifier" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_Amount ("Height Adjustment", Float) = 1.0
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert vertex:vert
struct Input {
float2 uv_MainTex;
};
// Access the shaderlab properties
float _Amount;
sampler2D _MainTex;
// Vertex modifier function
void vert (inout appdata_full v) {
// Do whatever you want with the "vertex" property of v here
v.vertex.z += _Amount;
}
// Surface shader function
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
}
Yes you can manipulate individual vertices based on their x/y/z coordinates - these are in the model's local coordinate system. Note that these are floating point values so you might want to avoid exact equality tests like == though - best to use an approximation within a certain error range. And yes, if statements are possible in shaders (as are most C language constructs - for loops, sin/modulo/exponents etc.). They do incur a performance hit, but don't worry about that too much for now - glad you've got something working!
Yes, you should never compare whether a floating point value is exactly equal to another value (in any program$$anonymous$$g language), because there is an inherent degree of approximation in floating point arithmetic (in layman's terms, exactly as you say "1.0" might actually be "0.99999987", or similar).
So, a better test might be to see whether the absolute difference between two floating point values is within a certain error tolerance. For example something like: if(abs(v.vertex.x - _Xpos) < 0.01 && ...
And, yes, debugging shaders is a pain, because they're executed on the GPU and you can't write handy "print" debug messages to see what's going on. I typically debug to see whether a certain block of code is being executed by making gross changes within that block (making the entire texture yellow, scaling the whole model by 20x or something).
Your tips are great and your help is so much appreciated :D I wouldn't have gotten half as far without you. $$anonymous$$udos!
I will try and test and publish my results here so future people who are as inexperienced with shaders as I am can learn from these posts.
Victory! (kudos to the participants in this threat, tanoshimi in particular) I've cast it to world space, and based on their coordinates (rounded) they will grow. These are my final results:
Shader "Custom/vertexShader_program" {
Properties {
_$$anonymous$$ainTex ("Base (RGB)", 2D) = "white" {}
_Amount ("Height", Float) = 1.0 //this will be incremented in script
_xPos ("xPos", float) = 5.0 //just to test, these can be filled from script with desired values.
_zPos ("zPos", float) = 3.0 //It might be wise to optimise this to a float2
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRA$$anonymous$$
#pragma surface surf Lambert vertex:vert
sampler2D _$$anonymous$$ainTex;
float _Amount;
float _xPos;
float _zPos;
struct Input {
float2 uv_$$anonymous$$ainTex;
};
void vert (inout appdata_full v)
{
float3 castToWorld = round(mul(_Object2World, v.vertex) );
if(castToWorld.x == 5.0 && castToWorld.y < _Amount && castToWorld.z == -1.0)
{
v.vertex.y+= _Amount;
}
}
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_$$anonymous$$ainTex, IN.uv_$$anonymous$$ainTex);
o.Albedo = tex2D (_$$anonymous$$ainTex, IN.uv_$$anonymous$$ainTex).rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
Script: using UnityEngine; using System.Collections;
public class ShaderVariables : $$anonymous$$onoBehaviour {
// Use this for initialization
GameObject target;
Shader targetShader;
$$anonymous$$aterial mat;
float desired = 5.0f;
void Start () {
target = GameObject.Find("Plane"); //get my plane, I will do this later on by creating primitives.
targetShader = target.GetComponent<$$anonymous$$eshRenderer>().material.shader; //get the shader, this is handy if you want to switch shaders.
mat = target.GetComponent<Renderer>().material; //and get my material, where I can send my variables to
}
// Update is called once per frame
void Update () {
float tempVar = mat.GetFloat("_Amount"); //ask for the amount of the float
Debug.Log( mat.GetFloat("_Amount") ); //Debugging to check
if(mat.GetFloat("_Amount") < desired) //and if "_Amount" is less than desired
{
mat.SetFloat("_Amount", tempVar+=0.1f); //I will increase the amount by 0.1 (you can manipulate 0.1 to change the grow speed;
}
}
}