Skip to content

Instantly share code, notes, and snippets.

@patricknelson
Forked from nkint/pointTowards.vertex
Last active May 11, 2023 15:49
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save patricknelson/f4dcaedda9eea5f5cf2c359f68aa35fd to your computer and use it in GitHub Desktop.
Save patricknelson/f4dcaedda9eea5f5cf2c359f68aa35fd to your computer and use it in GitHub Desktop.
Shader functions to facilitate rotation of vertex around point with a quaternion (Unity / HLSL / Cg)
// Full shader example demonstrating how to use a quaterionion to rotate a vertex around a specific point. Note that this is just a plain
// vanilla unlit shader which includes the necessary functions (see section below) and example code in the vertex shader.
//
// Forked from https://gist.github.com/nkint/7449c893fb7d6b5fa83118b8474d7dcb
// Converted from GLSL to Cg. For help with that, see https://alastaira.wordpress.com/2015/08/07/unity-shadertoys-a-k-a-converting-glsl-shaders-to-cghlsl/
//
// quaternion code from https://github.com/stackgl/gl-quat
// rotation from https://twistedpairdevelopment.wordpress.com/2013/02/11/rotating-a-vector-by-a-quaternion-in-glsl/
Shader "Unlit/Quaternion"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
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;
};
////////////////// BEGIN QUATERNION FUNCTIONS //////////////////
float PI = 3.1415926535897932384626433832795;
float4 setAxisAngle (float3 axis, float rad) {
rad = rad * 0.5;
float s = sin(rad);
return float4(s * axis[0], s * axis[1], s * axis[2], cos(rad));
}
float3 xUnitVec3 = float3(1.0, 0.0, 0.0);
float3 yUnitVec3 = float3(0.0, 1.0, 0.0);
float4 rotationTo (float3 a, float3 b) {
float vecDot = dot(a, b);
float3 tmpvec3 = float3(0, 0, 0);
if (vecDot < -0.999999) {
tmpvec3 = cross(xUnitVec3, a);
if (length(tmpvec3) < 0.000001) {
tmpvec3 = cross(yUnitVec3, a);
}
tmpvec3 = normalize(tmpvec3);
return setAxisAngle(tmpvec3, PI);
} else if (vecDot > 0.999999) {
return float4(0,0,0,1);
} else {
tmpvec3 = cross(a, b);
float4 _out = float4(tmpvec3[0], tmpvec3[1], tmpvec3[2], 1.0 + vecDot);
return normalize(_out);
}
}
float4 multQuat(float4 q1, float4 q2) {
return float4(
q1.w * q2.x + q1.x * q2.w + q1.z * q2.y - q1.y * q2.z,
q1.w * q2.y + q1.y * q2.w + q1.x * q2.z - q1.z * q2.x,
q1.w * q2.z + q1.z * q2.w + q1.y * q2.x - q1.x * q2.y,
q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
);
}
float3 rotateVector( float4 quat, float3 vec ) {
// https://twistedpairdevelopment.wordpress.com/2013/02/11/rotating-a-vector-by-a-quaternion-in-glsl/
float4 qv = multQuat( quat, float4(vec, 0.0) );
return multQuat( qv, float4(-quat.x, -quat.y, -quat.z, quat.w) ).xyz;
}
////////////////// END QUATERNION FUNCTIONS //////////////////
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// Point we're rotating around. Can be calculated from anywhere
float4 midPoint = float3(1, 2, 3, 1); // w = 1.0, https://forum.unity.com/threads/a-vertex-position-is-a-float4-what-does-the-4th-component-represent.152809/
// Direction of camera relative to object space.
// Good for reference: https://gist.github.com/unitycoder/c5847a82343a8e721035
float3 viewDir = UNITY_MATRIX_IT_MV[2].xyz;
// Use quaternion to perform rotation toward view direction relative to mid-point.
float4 quaternion = rotationTo(viewDir, v.normal); // normal = forward, in this case.
float3 offsetPoint = v.vertex - midPoint; // Offset so we can rotate relative to this point.
float3 rotatedPoint = rotateVector(quaternion, offsetPoint) + midPoint; // Rotate and return back to offset.
// Setup regular coordinates based on rotated point.
o.vertex = UnityObjectToClipPos(rotatedPoint);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment