Skip to content

Instantly share code, notes, and snippets.

@lyuma
Last active September 19, 2022 23:19
Show Gist options
  • Save lyuma/e755620e2abe2593de94aed5021cdd3f to your computer and use it in GitHub Desktop.
Save lyuma/e755620e2abe2593de94aed5021cdd3f to your computer and use it in GitHub Desktop.
// WIP Godot port. Lighting function not yet implemented. Clouds need #define and are not transparent.
/*
MIT License
Godot Port Copyright (c) 2022 Lyuma
Copyright (c) 2022 SCRN-VRC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
https://bgolus.medium.com/rendering-a-sphere-on-a-quad-13c92025570c
https://www.shadertoy.com/view/WtlGWr
https://www.shadertoy.com/view/3tyyDz
https://github.com/netri/Neitri-Unity-Shaders
*/
// Shader "SCRN/Dice" {
shader_type spatial;
render_mode skip_vertex_transform;
uniform bool use_depth_texture;
// Properties {
group_uniforms Circles_Color_Settings;
uniform sampler2D _CircleTex1 : source_color, hint_default_white;
uniform vec4 _CircleCol = vec4(3, 3, 3, 1);
uniform float _CircleTex1Scale = 7.0;
group_uniforms Frame_Color_Settings;
uniform sampler2D _Matcap1 : source_color, hint_default_white;
uniform vec4 _FrameCol = vec4(1.5, 1.5, 1.5, 1);
group_uniforms Dice_Settings;
uniform float _Smoothness : hint_range(0, 1) = 0.9;
uniform float _EdgeCut : hint_range(0, 2) = 0.781;
uniform float _EdgeRound : hint_range(0, 1) = 0.712;
group_uniforms Cloud_Settings;
// [KeywordEnum(On, Off)] _Cloud ("Cloud On", Float) = 0.0
uniform bool _Cloud_On = false;
uniform vec4 _CloudColor = vec4(0.5, 0.5, 0.5, 1);
uniform vec4 _CloudGlowCol = vec4(0.74, 0.0, 3.2, 1);
uniform float _CloudScale : hint_range(1.0, 10.0) = 3.18;
uniform float _CloudNoiseScale : hint_range(1.0, 10.0) = 9.46;
uniform float _CloudSharpness : hint_range(0.0, 20.0) = 8.4;
uniform float _CloudShadow : hint_range(0.0, 1.0) = 0.517;
uniform vec4 _CloudOffset = vec4(0.05, -0.04, 0.07, 0);
group_uniforms Other_Settings;
uniform vec4 _GlowCol = vec4(2.48, 0.0, 4.54, 1);
uniform vec4 _Test = vec4(0, 0, 0, 0);
uniform sampler2D _NoiseTex: hint_default_black;
uniform samplerCube _CubeTex: hint_default_black;
// Performance heavy.
//#define ENABLE_CLOUDS
// Godot forces shaders which access DEPTH_TEXTURE to transparent.
//#define USE_DEPTH_TEXTURE
// should make shadow receiving work on mobile
//#if defined(UNITY_float_PRECISION_FRAGMENT_SHADER_REGISTERS)
//#undef UNITY_float_PRECISION_FRAGMENT_SHADER_REGISTERS
//#endif
////#include "./AudioLink/AudioLink.cginc"
// real check needed for enabling conservative depth
// requires Shader Model 5.0
//#if SHADER_TARGET > 40
//#define USE_CONSERVATIVE_DEPTH 1
//#endif
//vec4 pos : SV_POSITION;
varying vec3 varying_rd;
varying vec3 varying_ro;
varying vec4 modelPos;
varying vec3 center;
varying float maxScale;
struct marchInOut
{
vec3 ro;
vec3 rd;
vec3 pos;
vec3 norm;
vec4 col;
float depth;
float matID;
float dist;
};
//#define DEBUG_VERTEX_SHADER
void vertex() {
// check if the current projection is orthographic or not from the current projection matrix
bool isOrtho = PROJECTION_MATRIX[3][3] > 0.5;
// viewer position, equivalent to _WorldSpaceCameraPos.xyz, but for the current view
vec3 worldSpaceViewerPos = (INV_VIEW_MATRIX)[3].xyz;
// view forward
vec3 worldSpaceViewForward = -(INV_VIEW_MATRIX)[2].xyz;
// pivot position
vec3 worldSpacePivotPos = (MODEL_MATRIX)[3].xyz;
// offset between pivot and camera
vec3 worldSpacePivotToView = worldSpacePivotPos - worldSpaceViewerPos;
// get the max object scale
vec3 scale = vec3(
length(transpose(MODEL_MATRIX)[0].xyz),
length(transpose(MODEL_MATRIX)[1].xyz),
length(transpose(MODEL_MATRIX)[2].xyz)
);
maxScale = max(abs(scale.x), max(abs(scale.y), abs(scale.z)));
// calculate a camera facing rotation matrix
vec3 up = (INV_VIEW_MATRIX)[1].xyz;
vec3 forward = isOrtho ? worldSpaceViewForward : normalize(worldSpacePivotToView);
vec3 right = normalize(cross(forward, up));
up = cross(right, forward);
mat3 quadOrientationMatrix = transpose(mat3(right, up, forward));
// use the max scale to figure out how big the quad needs to be to cover the entire sphere
// we're using a hardcoded object space radius of 0.5 in the fragment shader
float maxRadius = maxScale * 0.5;
// find the radius of a cone that contains the sphere with the point at the camera and the base at the pivot of the sphere
// this means the quad is always scaled to perfectly cover only the area the sphere is visible within
float quadScale = maxScale;
if (!isOrtho)
{
// get the sine of the right triangle with the hyp of the sphere pivot distance and the opp of the sphere radius
float sinAngle = maxRadius / length(worldSpacePivotToView);
// convert to cosine
float cosAngle = sqrt(1.0 - sinAngle * sinAngle);
// convert to tangent
float tanAngle = sinAngle / cosAngle;
// basically this, but should be faster
//tanAngle = tan(asin(sinAngle));
// get the opp of the right triangle with the 90 degree at the sphere pivot * 2
quadScale = tanAngle * length(worldSpacePivotToView) * 2.0;
}
vec3 vvertex = VERTEX;
// flatten mesh, in case it's a cube or sloped quad mesh
vvertex.z = 0.0;
// calculate world space position for the camera facing quad
vec3 worldPos = (vvertex.xyz * quadScale) * quadOrientationMatrix + worldSpacePivotPos;
VERTEX = (VIEW_MATRIX * vec4(worldPos, 1.0)).xyz;
NORMAL = mat3(VIEW_MATRIX) * quadOrientationMatrix * NORMAL;
TANGENT = mat3(VIEW_MATRIX) * quadOrientationMatrix * TANGENT;
//POSITION = PROJECTION_MATRIX * vec4(VERTEX, 1.0);
// calculate world space view ray direction and origin for perspective or orthographic
vec3 worldSpaceRayOrigin = worldSpaceViewerPos;
vec3 worldSpaceRayDir = worldPos - worldSpaceRayOrigin;
if (isOrtho)
{
worldSpaceRayDir = worldSpaceViewForward * -dot(worldSpacePivotToView, worldSpaceViewForward);
worldSpaceRayOrigin = worldPos - worldSpaceRayDir;
}
// scale the ray with the scale of the game object
worldSpaceRayDir = normalize(worldSpaceRayDir) * maxScale;
// output object space ray direction and origin
varying_rd = (inverse(MODEL_MATRIX) * vec4(worldSpaceRayDir, 0.0)).xyz;
varying_ro = (inverse(MODEL_MATRIX) * vec4(worldSpaceRayOrigin, 1.0)).xyz;
#if defined(USE_CONSERVATIVE_DEPTH)
worldPos += worldSpaceRayDir / dot(normalize(worldSpacePivotToView), worldSpaceRayDir) * maxRadius;
#endif
// setting up to read the depth pass
modelPos = (inverse(MODEL_MATRIX) * vec4(worldPos, 1.0));
center = (MODEL_MATRIX * vec4(0., 0., 0., 1.)).xyz;
}
// https://www.shadertoy.com/view/WtlGWr
// positions of all the dimples in the dice
const vec3 dips[21] =
{
// one
vec3( 0., 0., 0.31),
// two
vec3( 0.31, 0.0, 0.12),
vec3( 0.31, 0.0, -0.12),
// three
vec3( 0.12, 0.31, 0.12),
vec3( 0., 0.31, 0. ),
vec3(-0.12, 0.31, -0.12),
// four
vec3( 0.12, -0.31, 0.12),
vec3(-0.12, -0.31, 0.12),
vec3( 0.12, -0.31, -0.12),
vec3(-0.12, -0.31, -0.12),
// five
vec3(-0.31, 0., 0. ),
vec3(-0.31, -0.12, -0.12),
vec3(-0.31, -0.12, 0.12),
vec3(-0.31, 0.12, -0.12),
vec3(-0.31, 0.12, 0.12),
// six
vec3( 0.13, -0.13, -0.31),
vec3( 0.13, 0., -0.31),
vec3( 0.13, 0.13, -0.31),
vec3(-0.13, -0.13, -0.31),
vec3(-0.13, 0., -0.31),
vec3(-0.13, 0.13, -0.31)
};
const float dipsR[21] =
{
// one
0.12,
// two
0.08, 0.08,
// three
0.07, 0.07, 0.07,
// four
0.07, 0.07, 0.07, 0.07,
// five
0.06, 0.06, 0.06, 0.06, 0.06,
// six
0.06, 0.06, 0.06, 0.06, 0.06, 0.06
};
// https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/reflections/
vec3 BoxProjection(vec3 direction, vec3 position, vec4 cubemapPosition, vec3 boxMin, vec3 boxMax)
{
if (cubemapPosition.w > 0.0) {
vec3 factors = (mix(boxMin, boxMax, greaterThan(direction, vec3(0.0))) - position) / direction;
float scalar = min(min(factors.x, factors.y), factors.z);
direction = direction * scalar + (position - cubemapPosition.xyz);
}
return direction;
}
/*
vec3 refProbe(vec3 worldPos, vec3 reflVec)
{
vec3 boxProject = BoxProjection(reflVec, worldPos,
unity_SpecCube0_ProbePosition, unity_SpecCube0_BoxMin,
unity_SpecCube0_BoxMax);
float roughness = 1.0 - _Smoothness;
roughness *= 1.7 - 0.7 * roughness;
vec4 boxProbe0 = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, boxProject, roughness);
boxProbe0.rgb = DecodeHDR(boxProbe0, unity_SpecCube0_HDR);
vec3 indirectSpecular;
float blend = unity_SpecCube0_BoxMin.w;
[branch]
if (blend < 0.99999) {
vec3 boxProject = BoxProjection(
reflVec, worldPos,
unity_SpecCube1_ProbePosition,
unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax
);
vec4 boxProbe1 = UNITY_SAMPLE_TEXCUBE_SAMPLER_LOD(unity_SpecCube1, unity_SpecCube0, boxProject, roughness);
boxProbe1.rgb = DecodeHDR(boxProbe1, unity_SpecCube1_HDR);
indirectSpecular = mix(boxProbe1.rgb, boxProbe0.rgb, blend);
}
else
{
indirectSpecular = boxProbe0.rgb;
}
if (!any(indirectSpecular))
{
indirectSpecular = texCUBElod(_CubeTex, vec4(reflVec, roughness));
}
return indirectSpecular;
}
*/
vec3 refProbe(vec3 worldPos, vec3 reflVec) {
float roughness = 1.0 - _Smoothness;
roughness *= 1.7 - 0.7 * roughness;
return textureLod(_CubeTex, reflVec, roughness).rgb;
}
// https://mercury.sexy/hg_sdf/
// Shortcut for 45-degrees rotation
void pR45(inout vec2 p) {
p = (p + vec2(p.y, -p.x)) * sqrt(0.5);
}
// Repeat around the origin by a fixed angle.
// For easier use, num of repetitions is use to specify the angle.
float pModPolar(inout vec2 p, float repetitions) {
float angle = 2.0*PI/repetitions;
float a = atan(p.y, p.x) + angle/2.0;
float r = length(p);
float c = floor(a/angle);
a = mod(a,angle) - angle/2.0;
p = vec2(cos(a), sin(a))*r;
// For an odd number of repetitions, fix cell index of the cell in -x direction
// (cell index would be e.g. -5 and 5 in the two halves of the cell):
if (abs(c) >= (repetitions/2.0)) c = abs(c);
return c;
}
float fOpIntersectionChamfer(float a, float b, float r) {
return max(max(a, b), (a + r + b)*sqrt(0.5));
}
// Difference can be built from Intersection or Union:
float fOpDifferenceChamfer (float a, float b, float r) {
return fOpIntersectionChamfer(a, -b, r);
}
// https://www.shadertoy.com/view/wsSGDG
float sdOctahedron(vec3 p, float s) {
p = abs(p);
float m = (p.x + p.y + p.z - s) / 3.0;
vec3 o = p - m;
vec3 k = min(o, 0.0);
o = o + (k.x + k.y + k.z) * 0.5 - k * 1.5;
o = clamp(o, 0.0, s);
return length(p - o) * sign(m);
}
float sphere (vec3 p, float radius) {
return length(p) - radius ;
}
float box( vec3 p, vec3 b )
{
vec3 q = abs(p) - b;
return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}
/*
out:
x - distance of entire sdf
y - material ID
0 - default glass
1 - glowing border
2 - glowing circles
z - distance to glowing stuff
*/
vec3 mapDice(vec3 p)
{
float s = sdOctahedron(p, _EdgeRound);
float c = box(p, vec3(0.29));
float dice = fOpIntersectionChamfer(s, c, 0.003);
// carve the box edges away
vec3 p2 = p;
pR45(p2.xz);
p2.xz *= _EdgeCut;
float sc = box(p2, vec3(0.29));
dice = max(dice, sc);
p2 = p;
pR45(p2.xy);
p2.xy *= _EdgeCut;
sc = box(p2, vec3(0.29));
dice = max(dice, sc);
p2 = p;
pR45(p2.yz);
p2.yz *= _EdgeCut;
sc = box(p2, vec3(0.29));
dice = max(dice, sc);
// edge details
float bcut = box(p, vec3(0.27));
s = sdOctahedron(p, _EdgeRound + 0.02);
bcut = max(bcut, s);
float ccut = box(p, vec3(0.257));
ccut = fOpDifferenceChamfer(bcut, ccut, 0.005);
dice = min(dice, ccut);
float matID = 0.0;
float light = ccut * 5.0;
matID = dice == ccut ? 1.0 : matID;
// //short circuting for better performance
// if (dice > 0.01) return vec3(dice, matID, light);
float d = sphere(p + dips[0], dipsR[0]);
for (int i = 1; i < 21; i++) {
d = min(d, sphere(p + dips[i], dipsR[i]));
}
dice = max(dice, -d);
float dc = abs(dice + d - 0.01);
matID = dc < 0.01 ? 2.0 : matID;
matID += dc < 0.01 ? dc : 0.0; // store intensity
return vec3(dice, matID, light);
}
// faster 4 tap normals
vec3 diceNorm( in vec3 p ){
const vec2 e = vec2(0.0015, -0.0015);
return normalize(
e.xyy*mapDice(p+e.xyy).x +
e.yyx*mapDice(p+e.yyx).x +
e.yxy*mapDice(p+e.yxy).x +
e.xxx*mapDice(p+e.xxx).x);
}
void marchOuter(inout marchInOut mI, float max_steps)
{
vec3 p = mI.ro;
vec3 rd = mI.rd;
float t = 0.0;
bool hit = false;
for (float i = 0.; i < max_steps; i++) {
vec3 d = mapDice(p);
// more detail the closer
if (d.x < (0.0001 * (t + 1.0))) {
hit = true;
mI.matID = d.y;
break;
}
p += d.x * rd;
t += d.x;
if (any(greaterThan(abs(p), vec3(1.0)))) break;
}
mI.pos = p;
mI.col.a = hit ? 1.0 : 0.0;
mI.dist = t;
}
vec3 calculateWorldSpace(vec4 worldPos, vec4 screenPos, vec3 cameraPos, sampler2D depth_texture, mat4 xINV_PROJECTION_MATRIX)
{
// Subtract camera position from vertex position in world
// to get a ray pointing from the camera to this vertex.
vec3 worldDir = worldPos.xyz / worldPos.w - cameraPos;
// Calculate screen UV
vec2 screenUV = screenPos.xy / screenPos.w;
// _ProjectionParams.x is 1.0 (or –1.0 if currently rendering with a flipped projection matrix)
// screenUV.y *= _ProjectionParams.x;
screenUV = screenUV * 0.5f + 0.5f;
// Read depth, linearizing into worldspace units.
float scrdepth = textureLod(depth_texture, screenUV, 0.0).r;
vec4 upos = xINV_PROJECTION_MATRIX * vec4(screenUV * 2.0 - 1.0, scrdepth * 2.0 - 1.0, 1.0);
float depth = upos.z / upos.w; //LinearEyeDepth(UNITY_SAMPLE_DEPTH(SampleScreenDepth(screenUV))) / screenPos.w;
// Advance by depth along our view ray from the camera position.
// This is the worldspace coordinate of the corresponding fragment
// we retrieved from the depth buffer.
return worldDir * depth;
}
// https://github.com/keijiro/BiplanarMapping
// Biplanar mapping for color texture
void Biplanar_float
(sampler2D tex, vec3 wpos, vec3 wnrm, out vec4 output)
{
// Coordinate derivatives for texturing
vec3 p = wpos;
vec3 n = abs(wnrm);
vec3 dpdx = dFdx(p);
vec3 dpdy = dFdy(p);
// Major axis (in x; yz are following axis)
uvec3 ma = (n.x > n.y && n.x > n.z) ? uvec3(0, 1, 2) :
((n.y > n.z ) ? uvec3(1, 2, 0) :
uvec3(2, 0, 1)) ;
// Minor axis (in x; yz are following axis)
uvec3 mi = (n.x < n.y && n.x < n.z) ? uvec3(0, 1, 2) :
((n.y < n.z ) ? uvec3(1, 2, 0) :
uvec3(2, 0, 1)) ;
// Median axis (in x; yz are following axis)
uvec3 me = uvec3(3) - mi - ma;
// Project + fetch
vec4 x = textureGrad (tex,
vec2( p[ma.y], p[ma.z]),
vec2(dpdx[ma.y], dpdx[ma.z]),
vec2(dpdy[ma.y], dpdy[ma.z]));
vec4 y = textureGrad (tex,
vec2( p[me.y], p[me.z]),
vec2(dpdx[me.y], dpdx[me.z]),
vec2(dpdy[me.y], dpdy[me.z]));
// Blend factors
vec2 w = vec2(n[ma.x], n[me.x]);
// Make local support
w = clamp((w - 0.5773) / (1.0 - 0.5773), 0.0, 1.0);
// Blending
output = (x * w.x + y * w.y) / (w.x + w.y);
}
float noise(vec3 x) {
vec3 p = floor(x);
vec3 f = fract(x);
f = f*f*(3.0-2.0*f);
vec2 uv = (p.xy+vec2(37.0,239.0)*p.z) + f.xy;
vec2 rg = textureLod(_NoiseTex, vec2((uv+0.5)/256.0), 0).yx;
return mix( rg.x, rg.y, f.z )*2.0-1.0;
}
//https://iquilezles.org/articles/fbm
float fbm(in vec3 x) {
x -= TIME * _CloudOffset.xyz;
const float H = 1.0;
const int num_octaves = 5;
float G = exp2(-H);
float f = 1.0;
float a = 1.0;
float t = 0.0;
for(int i=0; i<num_octaves; i++) {
t += (i>2) ? a*noise(f*(x)): a*noise(f*x);
f *= 2.0;
a *= G;
}
return t;
}
float mapClouds(vec3 po) {
// if it's outside the dice bounds, ease off the density
float c = box(po, vec3(0.2));
c = clamp(-(c * 0.5 - 0.5), 0.0, 1.0);
float sd = sphere(po, -_CloudScale);
sd += c * (_CloudSharpness * fbm(po * _CloudNoiseScale));
return sd;
}
// faster 4 tap normals
vec3 cloudNorm( in vec3 p ){
const vec2 e = vec2(0.01, -0.01);
return normalize(
e.xyy*mapClouds(p+e.xyy) +
e.yyx*mapClouds(p+e.yyx) +
e.yxy*mapClouds(p+e.yxy) +
e.xxx*mapClouds(p+e.xxx));
}
struct LightInfo {
vec3 dir;
vec3 color;
bool isDirectionalLight;
};
// https://www.shadertoy.com/view/sdjcRD
vec4 marchClouds( vec3 ro, inout vec3 rd, float mind, float maxd, float maxs, LightInfo light, float audio1)
{
vec3 cloudColor = _CloudColor.rgb * _CloudColor.a;
vec3 lightVec = light.isDirectionalLight ? vec3(0.5, 0.0, 0.5) : light.dir;
const float steps = maxs;
const float shadowSteps = 10.;
const float invSteps = 1. / (steps);
const float invShadowSteps = 0.75 / (shadowSteps);
const float stepDistance = maxd * invSteps;
const float shadowStepSize = 0.75 * invShadowSteps;
vec3 lightColor = vec3(0.,0.,0.);
float lightPower = 1.0;
vec3 CurPos = ro;
float li = 0.0;
float t = mind;
for(float I = 0.0; I < steps; ++I) {
if (t > maxd) break;
float cursample = mapClouds( CurPos ) * 3.0;
if ( cursample > 0.01 ) {
vec3 lpos = CurPos;
float shadowDist = 0.;
for ( float S = 0.0 ; S < shadowSteps ; ++S ) {
lpos += lightVec * shadowStepSize ;
float lsample = (mapClouds( lpos ));
shadowDist += lsample * _CloudShadow;
}
float curdensity = clamp( cursample * invSteps, 0.0, 1.0 );
float shadow = exp( - shadowDist * invShadowSteps );
lightColor += shadow * curdensity * lightPower * (cloudColor);
lightPower *= (1. - curdensity);
// sample the dice again for the glow
float glow = mapDice(CurPos).z;
li += 0.1 / (1.0 + glow * glow * (200.)) * lightPower;
if ( lightPower < 0.01 ) {
break;
}
}
t += min(0.02 * exp(-0.2 * cursample), stepDistance);
CurPos = ro + rd * t;
}
float lightScale = dot(light.color, vec3(0.2125, 0.7154, 0.0721));
lightScale = smoothstep(0.0, 2.0, 1.0 - (lightScale - 1.0));
lightColor.rgb += _CloudGlowCol.rgb * (lightScale * _CloudGlowCol.a * li * (audio1 * 0.5 + 0.5));
return vec4( lightColor , 1.0 - lightPower );
}
// https://www.shadertoy.com/view/WsXSDH
// cheap AO using normals
float diceCheapAO(vec3 p, vec3 n)
{
float a = .5+.5*mapDice(p+n*.04).x/.05;
a *= .6+.4*mapDice(p+n*.08).x/.1;
a *= .7+.3*mapDice(p+n*.16).x/.2;
return clamp(a * a, 0.0, 1.0);
}
// same idea for the AO
// since we know where the surface is,
// just sample from the surface back to the origin for a nice, cheap glow effect
vec3 diceCheapGlow(vec3 p, vec3 rd, float audio1)
{
float li = 0.0;
for (float i = 0.0; i < 5.0; i++)
{
vec3 d = mapDice(p + rd * (-0.1 * i));
li += 0.1 / (1.0 + d.z * d.z * (1000.0 - 500.0 * audio1));
}
return _GlowCol.rgb * li * _GlowCol.a;
}
vec3 applyMat(float matID, vec3 pos, vec3 viewDir,
vec3 norm, vec3 inCol, vec3 effects, float audio2)
{
vec3 col = inCol;
// glass
if (matID < 1.0);
// dice frame
else if (matID < 2.0)
{
// poi's matcap code from their toon shadur
vec3 worldViewUp = normalize(vec3(0, 1, 0) - viewDir * dot(viewDir, vec3(0, 1, 0)));
vec3 worldViewRight = normalize(cross(viewDir, worldViewUp));
vec2 matcapUV = vec2(dot(worldViewRight, norm), dot(worldViewUp, norm)) * 0.5 + 0.5;
vec4 matcapCol = texture(_Matcap1, matcapUV);
col = (matcapCol.rgb * _FrameCol.rgb) * effects * _FrameCol.a;
}
// circles
else if (matID < 3.0)
{
vec4 biOut;
// biplanar mapping
Biplanar_float(_CircleTex1,
pos * _CircleTex1Scale, norm, biOut);
biOut *= _CircleCol;
float scale = clamp(1.0 - (matID - floor(matID)) / 0.01, 0.0, 1.0);
float aa = 0.7 * fwidth(scale);
col = mix(col, biOut.rgb * effects,
smoothstep(0.75 - aa - audio2 * 0.25, 1.0 + aa - audio2 * 0.25, scale) * biOut.a);
}
return col;
}
void marchInner(inout marchInOut mI, LightInfo light, float max_steps, float audio1, float audio2, mat4 xMODEL_MATRIX)
{
const float cref = 0.95;
vec3 col = mI.col.rgb;
vec3 iniPos = mI.ro;
vec3 iniDir = mI.rd;
vec3 n = mI.norm;
float iniMat = mI.matID;
vec3 refl = reflect(mI.rd, n);
vec3 refr = refract(mI.rd, n, cref);
// march into the back of the dice
mI.ro = mI.ro + refr;
mI.rd = -refr;
marchOuter(mI, 32.0);
vec3 nout = diceNorm(mI.pos);
float dout = mI.dist;
vec4 c = vec4(0.);
if (_Cloud_On) {
dout = 1.0 - dout;
#if defined(ENABLE_CLOUDS)
c = marchClouds(iniPos, iniDir, 0., dout, max_steps, light, audio1);
#endif
}
vec3 refrOut = refract(-mI.rd, nout, cref);
refrOut = mat3(xMODEL_MATRIX) * (refrOut);
vec3 iniWorldPos = (xMODEL_MATRIX * vec4(iniPos, 1.0)).xyz;
vec3 reflWorldPos = (xMODEL_MATRIX * vec4(mI.pos, 1.0)).xyz;
// do colors
// inside
float ao = diceCheapAO(iniPos, n);
vec3 colInner = refProbe(reflWorldPos, refrOut);
if (_Cloud_On) {
colInner *= 0.5;
}
colInner = applyMat(mI.matID, mI.pos, mI.rd, nout, colInner, vec3(ao * (1. - c.a)), audio2);
col = mix(colInner, c.rgb, c.a);
// outside
col = applyMat(iniMat, iniPos, iniDir, n, col, vec3(ao), audio2);
mI.col = vec4(col, 1.0);
mI.matID = iniMat;
mI.rd = iniDir;
mI.norm = n;
}
// reuse the fragment shader for both forward base and forward add passes
void fragment () {
#ifndef DEBUG_VERTEX_SHADER
marchInOut mI;
mI.ro = varying_ro;
mI.rd = varying_rd;
mI.pos = vec3(0., 0., 0.);
mI.norm = vec3(0., 1., 0.);
mI.col = vec4(0., 0., 0., 1.);
mI.depth = 0.0;
mI.matID = 0.0;
mI.dist = 0.0;
vec3 worldPos;
vec3 normal;
vec3 surfacePos;
vec4 clipPos;
// If there's no shadow pass just do the ray march
/*if (DepthTextureExists())*/
#if !defined(USE_DEPTH_TEXTURE)
//if (!use_depth_texture){
marchOuter(mI, 200.0);
if(mI.col.a < 0.01) {
discard;
}
surfacePos = mI.pos;
worldPos = (MODEL_MATRIX * vec4(surfacePos, 1.0)).xyz;
clipPos = PROJECTION_MATRIX * VIEW_MATRIX * vec4(worldPos, 1.0);
//}
// If there is a shadow pass reconstruct the world position
#else
{
// https://github.com/netri/Neitri-Unity-Shaders/blob/master/World%20Normal%20Nice%20Slow.shader
// get the world position from the depth pass
//vec4 screenPos = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * modelPos;
// Transform from adjusted screen pos back to world pos
//worldPos = calculateWorldSpace(INV_VIEW_MATRIX * INV_PROJECTION_MATRIX * screenPos, screenPos, (INV_VIEW_MATRIX)[3].xyz, DEPTH_TEXTURE, INV_PROJECTION_MATRIX) + (INV_VIEW_MATRIX)[3].xyz;
//float scrdepth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r;
//vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, scrdepth, 1.0); // * 2.0 - 1.0
//worldPos = upos.xyz / upos.w;
float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
vec4 ndc = vec4(SCREEN_UV * 2.0 - 1.0, depth , 1) ;
vec4 view = INV_PROJECTION_MATRIX * ndc;
view /= view.w;
worldPos = (INV_VIEW_MATRIX * view).xyz;
worldPos += normalize((INV_VIEW_MATRIX)[2]).xyz * 0.0001;
// check the SDF to discard other geometry based on view distance
clipPos = PROJECTION_MATRIX * VIEW_MATRIX * vec4(worldPos, 1.0);
surfacePos = (inverse(MODEL_MATRIX) * vec4(worldPos, 1.0)).xyz;
vec3 dist = mapDice(surfacePos);
mI.matID = dist.y;
float checkDist = 0.0005 / maxScale * (1.0 + distance(worldPos, transpose(INV_VIEW_MATRIX)[3].xyz));
if (dist.x > checkDist) discard;
}
#endif
normal = diceNorm(surfacePos);
mI.ro = surfacePos;
mI.norm = normal;
mI.depth = clipPos.z / clipPos.w;
float shadowIN = 0.0;
/*
// stuff for directional shadow receiving
#if defined (SHADOWS_SCREEN)
// setup shadow struct for screen space shadows
shadowInput shadowIN;
#if defined(UNITY_NO_SCREENSPACE_SHADOWS)
// mobile directional shadow
shadowIN._ShadowCoord = mul(unity_WorldToShadow[0], vec4(worldPos, 1.0));
#else
// screen space directional shadow
shadowIN._ShadowCoord = ComputeScreenPos(clipPos);
#endif // UNITY_NO_SCREENSPACE_SHADOWS
#else
// no shadow, or no directional shadow
shadowIN = 0.0;
#endif // SHADOWS_SCREEN
*/
NORMAL = mat3(VIEW_MATRIX) * normal;
// basic lighting
/*
vec3 worldNormal = mat3(MODEL_MATRIX) * normal;
vec3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
vec3 worldDir = UnityObjectToWorldDir(mI.rd);
float ndotl = saturate(dot(worldNormal, worldLightDir));
// get shadow, attenuation, and cookie
UNITY_LIGHT_ATTENUATION(atten, shadowIN, worldPos);
// per pixel lighting
vec3 lighting = _LightColor0 * atten * ndotl;
vec3 vertexLighting = vec3(0.);
*/
/*
#if defined(VERTEXLIGHT_ON)
// "per vertex" non-important lights
vertexLighting = Shade4PointLights(
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, worldPos, worldNormal);
lighting += vertexLighting;
#endif // VERTEXLIGHT_ON
*/
// audiolink
float audio1 =(sin(TIME * 10.0) + 1.0) * 0.5;
float audio2 =(sin(TIME * 14.23) + 0.6) * 0.2;
//audio1 = (sin(_Time.y * 10) + 1.0) * 0.5;
//audio1 = AudioLinkData( ALPASS_AUDIOLINK + int2( 0, 0 ) ).r;
//audio2 = AudioLinkData( ALPASS_AUDIOLINK + int2( 0, 2 ) ).r * 2.0;
LightInfo light;
light.isDirectionalLight = true;
light.color = vec3(0.72,0.6,0.9); //_LightColor0 + vertexLighting;
// no point lights in foward base, so just make everything directional
// from the center
light.dir = vec3(0.86,0.5,0.0); //normalize(UnityWorldSpaceLightDir(i.center));
marchInner(mI, light, 64., audio1, audio2, MODEL_MATRIX);
//light.color = lighting;
//light.dir = worldLightDir;
//apply lighting
//vec3 specularTint;
//float oneMinusReflectivity;
ROUGHNESS = 1.0 - _Smoothness;
METALLIC = (mI.matID == 1.0) ? 0.7 : 0.2;
//vec3 albedo = DiffuseAndSpecularFromMetallic(
// mI.col.rgb, metallic, specularTint, oneMinusReflectivity
//);
ALBEDO = mI.col.rgb;
/*
UnityIndirect indirectLight;
#ifdef UNITY_PASS_FORWARDADD
indirectLight.diffuse = indirectLight.specular = 0;
#else
indirectLight.diffuse = max(0, ShadeSH9(vec4(worldNormal, 1)));
vec3 reflectionDir = reflect(-worldDir, worldNormal);
Unity_GlossyEnvironmentData envData;
envData.roughness = 1 - smoothness;
envData.reflUVW = reflectionDir;
indirectLight.specular = Unity_GlossyEnvironment(
UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, envData
);
#endif
mI.col.rgb = UNITY_BRDF_PBS(
albedo, specularTint,
oneMinusReflectivity, smoothness,
worldNormal, -worldDir,
light, indirectLight
);
*/
//#ifdef UNITY_PASS_FORWARDBASE
vec3 glow = diceCheapGlow(surfacePos, mI.rd, audio1);
glow.rgb = glow.rgb * (1.0 + audio1);
mI.col.rgb += glow * ((mI.matID == 1.0) ? 0.1 : 1.0);
//#endif
DEPTH = mI.depth;
// fog
//float fogCoord = clipPos.z;
//UNITY_APPLY_FOG(fogCoord, mI.col);
ALBEDO = mI.col.rgb;
#endif
}
/*
void frag_shadow_only () {
marchInOut mI;
mI.ro = varying_ro;
mI.rd = varying_rd;
mI.pos = vec3(0., 0., 0.);
mI.norm = vec3(0., 1., 0.);
mI.col = vec4(1., 1., 1., 1.);
mI.depth = 0.0;
mI.matID = 0.0;
mI.dist = 0.0;
marchOuter(mI, 200.0);
if (mI.col.a < 0.01) {
discard;
}
vec3 surfacePos = mI.pos;
// output modified depth
//vec4 clipPos = UnityClipSpaceShadowCasterPos(surfacePos, surfacePos);
//clipPos = UnityApplyLinearShadowBias(clipPos);
//outDepth = clipPos.z / clipPos.w;
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment