hey I’m trying to make a shader that leaves some parabolic ripple behind (or better in front) an object that gets dragged along the surface.
my two questions are:
- how can i get a more smooth result?
- how can i dynamically set the length of my array in the shader?
I calculate the ripples in a C# script and try to translate them to my shader.
With some luck i was able to generate the first ripple for now:

but as you can see it’s very choppy…
I’m very new to Shaders. But i would expect a more smooth displacement.
I’m doing it very rough in the shader so maybe thats the problem, but i have no clue if i can do it differently?
also I’m having a hard time to use _Length as the resolution for my Array.
I read i can use #define _Length but same here no idea how to set the value for this from my script.
the Shader:
Shader "Custom/Parabola" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_Size ("Size", float) = 1
_Speed ("Speed", float) = 1
_Amp ("Amplitude", float) = 1
_Length ("Resolution", int) = 1
//_Points("Points", vector[])
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Upgrade NOTE: excluded shader from DX11, OpenGL ES 2.0 because it uses unsized arrays
#pragma exclude_renderers d3d11 gles
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows vertex:vert addshadow
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
half _Size;
half _Speed;
half _Amp;
fixed4 _Color;
int _Length;
float4 _Points[200];
// 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_CBUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_CBUFFER_END
float4 getNewVertPosition(float4 p)
{
float3 worldPos = mul(unity_ObjectToWorld, p).xyz;
for(int i=0; i<_Length; i++)
{
if(worldPos.z <= (_Points[i].z+_Size) && worldPos.z >= (_Points[i].z-_Size) &&
worldPos.x <= (_Points[i].x+_Size) && worldPos.x >= (_Points[i].x-_Size))
{
p.y += _Amp;
//v.normal.xyz += log10(sin(v.vertex.y)+_Amp);
}
}
return p;
}
void vert( inout appdata_full v )
{
float4 vertPosition = getNewVertPosition( v.vertex );
float4 bitangent = float4( cross( v.normal, v.tangent ), 0 );
float vertOffset = 0.01;
float4 v1 = getNewVertPosition( v.vertex + v.tangent * vertOffset );
float4 v2 = getNewVertPosition( v.vertex + bitangent * vertOffset );
float4 newTangent = v1 - vertPosition;
float4 newBitangent = v2 - vertPosition;
v.normal = cross( newTangent, newBitangent );
v.vertex = vertPosition;
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
the Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Parabola : MonoBehaviour {
public GameObject linePrefab, pointV;
public float offset, amp, speed;
public int freq;
public float directrixLength;
public int resolution;
private Vector3 mouse, prev;
private Vector3 directrix;
private Vector3 focusPoint, directrixPointMid, directrixPointL, directrixPointR;
private Vector3 focalLine;
private Vector3 p;
private List<List<Vector3>> pointsR = new List<List<Vector3>>();
private List<LineRenderer> lines = new List<LineRenderer>();
public Renderer render;
void Start ()
{
render.material.SetInt("_Length", resolution);
render.material.SetVectorArray("_Points", new Vector4[resolution]);
prev = mouse;
int i = 0;
while(i <= freq)
{
GameObject g = Instantiate(linePrefab);
g.transform.SetParent(transform);
g.transform.localPosition = Vector3.zero;
LineRenderer line = g.GetComponent<LineRenderer>();
line.positionCount = resolution;
lines.Add(line);
List<Vector3> lR = new List<Vector3>();
pointsR.Add(lR);
i++;
}
}
void FixedUpdate ()
{
if(Input.GetMouseButton(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, Mathf.Infinity, 1<<LayerMask.NameToLayer("Plane")))
{
mouse = new Vector3(hit.point.x,0,hit.point.z);
StopCoroutine(CalculateParabola(mouse));
StartCoroutine(CalculateParabola(mouse));
transform.position = mouse;
transform.forward = -focalLine;
}
}
}
IEnumerator CalculateParabola(Vector3 v)
{
while(prev != v)
{
focalLine = prev - v;
for(int h=0; h<=freq; h++)
{
pointsR[h] = new List<Vector3>();
float s = Vector3.Distance(prev.normalized,v.normalized)*speed;
for(float i = -directrixLength/2; i<=(directrixLength+1)/2; i+=directrixLength/resolution)
{
p.x = pointV.transform.localPosition.x+(i*(freq-h+Time.fixedDeltaTime*s));
p.z = ((amp*(freq-h))*(Time.fixedDeltaTime*s))*-Mathf.Pow(p.x,2)+(1+offset*h);
pointsR[h].Add(p);
}
lines[h].SetPositions(pointsR[h].ToArray());
Vector4[] renderPoints = new Vector4[resolution];
for(int i=0; i<resolution; i++)
{
renderPoints[i] = transform.TransformPoint(pointsR[0][i]);
}
render.material.SetVectorArray("_Points", renderPoints);
}
yield return new WaitForSeconds(0.1f);
prev = v;
}
}
}
Thx,
J
