-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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