Skip to content

Commit fa6b0c8

Browse files
committed
WetStone Shader example
1 parent 45b2250 commit fa6b0c8

File tree

2 files changed

+336
-0
lines changed

2 files changed

+336
-0
lines changed
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
2+
#version 150
3+
4+
#define SAMPLER0 sampler2D // sampler2D, sampler3D, samplerCube
5+
#define SAMPLER1 sampler2D // sampler2D, sampler3D, samplerCube
6+
#define SAMPLER2 sampler2D // sampler2D, sampler3D, samplerCube
7+
#define SAMPLER3 sampler2D // sampler2D, sampler3D, samplerCube
8+
9+
uniform SAMPLER0 iChannel0; // image/buffer/sound Sampler for input textures 0
10+
uniform SAMPLER1 iChannel1; // image/buffer/sound Sampler for input textures 1
11+
uniform SAMPLER2 iChannel2; // image/buffer/sound Sampler for input textures 2
12+
uniform SAMPLER3 iChannel3; // image/buffer/sound Sampler for input textures 3
13+
14+
uniform vec3 iResolution; // image/buffer The viewport resolution (z is pixel aspect ratio, usually 1.0)
15+
uniform float iTime; // image/sound/buffer Current time in seconds
16+
uniform float iTimeDelta; // image/buffer Time it takes to render a frame, in seconds
17+
uniform int iFrame; // image/buffer Current frame
18+
uniform float iFrameRate; // image/buffer Number of frames rendered per second
19+
uniform vec4 iMouse; // image/buffer xy = current pixel coords (if LMB is down). zw = click pixel
20+
uniform vec4 iDate; // image/buffer/sound Year, month, day, time in seconds in .xyzw
21+
uniform float iSampleRate; // image/buffer/sound The sound sample rate (typically 44100)
22+
uniform float iChannelTime[4]; // image/buffer Time for channel (if video or sound), in seconds
23+
uniform vec3 iChannelResolution[4]; // image/buffer/sound Input texture resolution for each channel
24+
25+
26+
27+
28+
//
29+
// Shadertoy Demo: https://www.shadertoy.com/view/ldSSzV
30+
// Shadertoy Author: https://www.shadertoy.com/user/TDM
31+
//
32+
33+
34+
/*
35+
"Wet stone" by Alexander Alekseev aka TDM - 2014
36+
License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
37+
Contact: tdmaav@gmail.com
38+
*/
39+
40+
#define SMOOTH
41+
42+
const int NUM_STEPS = 32;
43+
const int AO_SAMPLES = 3;
44+
const vec2 AO_PARAM = vec2(1.2, 3.8);
45+
const vec2 CORNER_PARAM = vec2(0.25, 40.0);
46+
const float INV_AO_SAMPLES = 1.0 / float(AO_SAMPLES);
47+
const float TRESHOLD = 0.1;
48+
const float EPSILON = 1e-3;
49+
const float LIGHT_INTENSITY = 0.25;
50+
const vec3 RED = vec3(1.0,0.7,0.7) * LIGHT_INTENSITY;
51+
const vec3 ORANGE = vec3(1.0,0.67,0.43) * LIGHT_INTENSITY;
52+
const vec3 BLUE = vec3(0.54,0.77,1.0) * LIGHT_INTENSITY;
53+
const vec3 WHITE = vec3(1.2,1.07,0.98) * LIGHT_INTENSITY;
54+
55+
const float DISPLACEMENT = 0.1;
56+
57+
// math
58+
mat3 fromEuler(vec3 ang) {
59+
vec2 a1 = vec2(sin(ang.x),cos(ang.x));
60+
vec2 a2 = vec2(sin(ang.y),cos(ang.y));
61+
vec2 a3 = vec2(sin(ang.z),cos(ang.z));
62+
mat3 m;
63+
m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);
64+
m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
65+
m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);
66+
return m;
67+
}
68+
float hash11(float p) {
69+
return fract(sin(p * 727.1)*435.545);
70+
}
71+
float hash12(vec2 p) {
72+
float h = dot(p,vec2(127.1,311.7));
73+
return fract(sin(h)*437.545);
74+
}
75+
vec3 hash31(float p) {
76+
vec3 h = vec3(127.231,491.7,718.423) * p;
77+
return fract(sin(h)*435.543);
78+
}
79+
80+
// 3d noise
81+
float noise_3(in vec3 p) {
82+
vec3 i = floor(p);
83+
vec3 f = fract(p);
84+
vec3 u = f*f*(3.0-2.0*f);
85+
86+
vec2 ii = i.xy + i.z * vec2(5.0);
87+
float a = hash12( ii + vec2(0.0,0.0) );
88+
float b = hash12( ii + vec2(1.0,0.0) );
89+
float c = hash12( ii + vec2(0.0,1.0) );
90+
float d = hash12( ii + vec2(1.0,1.0) );
91+
float v1 = mix(mix(a,b,u.x), mix(c,d,u.x), u.y);
92+
93+
ii += vec2(5.0);
94+
a = hash12( ii + vec2(0.0,0.0) );
95+
b = hash12( ii + vec2(1.0,0.0) );
96+
c = hash12( ii + vec2(0.0,1.0) );
97+
d = hash12( ii + vec2(1.0,1.0) );
98+
float v2 = mix(mix(a,b,u.x), mix(c,d,u.x), u.y);
99+
100+
return max(mix(v1,v2,u.z),0.0);
101+
}
102+
103+
// fBm
104+
float fbm3(vec3 p, float a, float f) {
105+
return noise_3(p);
106+
}
107+
108+
float fbm3_high(vec3 p, float a, float f) {
109+
float ret = 0.0;
110+
float amp = 1.0;
111+
float frq = 1.0;
112+
for(int i = 0; i < 4; i++) {
113+
float n = pow(noise_3(p * frq),2.0);
114+
ret += n * amp;
115+
frq *= f;
116+
amp *= a * (pow(n,0.2));
117+
}
118+
return ret;
119+
}
120+
121+
// lighting
122+
float diffuse(vec3 n,vec3 l,float p) { return pow(max(dot(n,l),0.0),p); }
123+
float specular(vec3 n,vec3 l,vec3 e,float s) {
124+
float nrm = (s + 8.0) / (3.1415 * 8.0);
125+
return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
126+
}
127+
128+
// distance functions
129+
float plane(vec3 gp, vec4 p) {
130+
return dot(p.xyz,gp+p.xyz*p.w);
131+
}
132+
float sphere(vec3 p,float r) {
133+
return length(p)-r;
134+
}
135+
float capsule(vec3 p,float r,float h) {
136+
p.y -= clamp(p.y,-h,h);
137+
return length(p)-r;
138+
}
139+
float cylinder(vec3 p,float r,float h) {
140+
return max(abs(p.y/h),capsule(p,r,h));
141+
}
142+
float box(vec3 p,vec3 s) {
143+
p = abs(p)-s;
144+
return max(max(p.x,p.y),p.z);
145+
}
146+
float rbox(vec3 p,vec3 s) {
147+
p = abs(p)-s;
148+
return length(p-min(p,0.0));
149+
}
150+
float quad(vec3 p,vec2 s) {
151+
p = abs(p) - vec3(s.x,0.0,s.y);
152+
return max(max(p.x,p.y),p.z);
153+
}
154+
155+
// boolean operations
156+
float boolUnion(float a,float b) { return min(a,b); }
157+
float boolIntersect(float a,float b) { return max(a,b); }
158+
float boolSub(float a,float b) { return max(a,-b); }
159+
160+
// smooth operations. thanks to iq
161+
float boolSmoothIntersect(float a, float b, float k ) {
162+
float h = clamp(0.5+0.5*(b-a)/k, 0.0, 1.0);
163+
return mix(a,b,h) + k*h*(1.0-h);
164+
}
165+
float boolSmoothSub(float a, float b, float k ) {
166+
return boolSmoothIntersect(a,-b,k);
167+
}
168+
169+
// world
170+
float rock(vec3 p) {
171+
float d = sphere(p,1.0);
172+
for(int i = 0; i < 9; i++) {
173+
float ii = float(i);
174+
float r = 2.5 + hash11(ii);
175+
vec3 v = normalize(hash31(ii) * 2.0 - 1.0);
176+
#ifdef SMOOTH
177+
d = boolSmoothSub(d,sphere(p+v*r,r * 0.8), 0.03);
178+
#else
179+
d = boolSub(d,sphere(p+v*r,r * 0.8));
180+
#endif
181+
}
182+
return d;
183+
}
184+
185+
float map(vec3 p) {
186+
float d = rock(p) + fbm3(p*4.0,0.4,2.96) * DISPLACEMENT;
187+
d = boolUnion(d,plane(p,vec4(0.0,1.0,0.0,1.0)));
188+
return d;
189+
}
190+
191+
float map_detailed(vec3 p) {
192+
float d = rock(p) + fbm3_high(p*4.0,0.4,2.96) * DISPLACEMENT;
193+
d = boolUnion(d,plane(p,vec4(0.0,1.0,0.0,1.0)));
194+
return d;
195+
}
196+
197+
// tracing
198+
vec3 getNormal(vec3 p, float dens) {
199+
vec3 n;
200+
n.x = map_detailed(vec3(p.x+EPSILON,p.y,p.z));
201+
n.y = map_detailed(vec3(p.x,p.y+EPSILON,p.z));
202+
n.z = map_detailed(vec3(p.x,p.y,p.z+EPSILON));
203+
return normalize(n-map_detailed(p));
204+
}
205+
vec2 getOcclusion(vec3 p, vec3 n) {
206+
vec2 r = vec2(0.0);
207+
for(int i = 0; i < AO_SAMPLES; i++) {
208+
float f = float(i)*INV_AO_SAMPLES;
209+
float hao = 0.01+f*AO_PARAM.x;
210+
float hc = 0.01+f*CORNER_PARAM.x;
211+
float dao = map(p + n * hao) - TRESHOLD;
212+
float dc = map(p - n * hc) - TRESHOLD;
213+
r.x += clamp(hao-dao,0.0,1.0) * (1.0-f);
214+
r.y += clamp(hc+dc,0.0,1.0) * (1.0-f);
215+
}
216+
r.x = pow(clamp(1.0-r.x*INV_AO_SAMPLES*AO_PARAM.y,0.0,1.0),0.5);
217+
r.y = clamp(r.y*INV_AO_SAMPLES*CORNER_PARAM.y,0.0,1.0);
218+
return r;
219+
}
220+
vec2 spheretracing(vec3 ori, vec3 dir, out vec3 p) {
221+
vec2 td = vec2(0.0);
222+
for(int i = 0; i < NUM_STEPS; i++) {
223+
p = ori + dir * td.x;
224+
td.y = map(p);
225+
if(td.y < TRESHOLD) break;
226+
td.x += (td.y-TRESHOLD) * 0.9;
227+
}
228+
return td;
229+
}
230+
231+
// stone
232+
vec3 getStoneColor(vec3 p, float c, vec3 l, vec3 n, vec3 e) {
233+
c = min(c + pow(noise_3(vec3(p.x*20.0,0.0,p.z*20.0)),70.0) * 8.0, 1.0);
234+
float ic = pow(1.0-c,0.5);
235+
vec3 base = vec3(0.42,0.3,0.2) * 0.6;
236+
vec3 sand = vec3(0.51,0.41,0.32);
237+
vec3 color = mix(base,sand,c);
238+
239+
float f = pow(1.0 - max(dot(n,-e),0.0), 1.5) * 0.75 * ic;
240+
color = mix(color,vec3(1.0),f);
241+
color += vec3(diffuse(n,l,0.5) * WHITE);
242+
color += vec3(specular(n,l,e,8.0) * WHITE * 1.5 * ic);
243+
n = normalize(n - normalize(p) * 0.4);
244+
color += vec3(specular(n,l,e,80.0) * WHITE * 1.5 * ic);
245+
return color;
246+
}
247+
248+
// main
249+
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
250+
vec2 iuv = fragCoord.xy / iResolution.xy * 2.0 - 1.0;
251+
vec2 uv = iuv;
252+
uv.x *= iResolution.x / iResolution.y;
253+
float time = iTime * 0.3;
254+
255+
// ray
256+
vec3 ang = vec3(0.0,0.2,time);
257+
if(iMouse.z > 0.0) ang = vec3(0.0,clamp(2.0-iMouse.y*0.01,0.0,3.1415),iMouse.x*0.01);
258+
mat3 rot = fromEuler(ang);
259+
260+
vec3 ori = vec3(0.0,0.0,2.8);
261+
vec3 dir = normalize(vec3(uv.xy,-2.0));
262+
ori = ori * rot;
263+
dir = dir * rot;
264+
265+
// tracing
266+
vec3 p;
267+
vec2 td = spheretracing(ori,dir,p);
268+
vec3 n = getNormal(p,td.y);
269+
vec2 occ = getOcclusion(p,n);
270+
vec3 light = normalize(vec3(0.0,1.0,0.0));
271+
272+
// color
273+
vec3 color = vec3(1.0);
274+
if(td.x < 3.5 && p.y > -0.89) color = getStoneColor(p,occ.y,light,n,dir);
275+
color *= occ.x;
276+
277+
// post
278+
float vgn = smoothstep(1.2,0.7,abs(iuv.y)) * smoothstep(1.1,0.8,abs(iuv.x));
279+
color *= 1.0 - (1.0 - vgn) * 0.15;
280+
fragColor = vec4(color,1.0);
281+
}
282+
283+
284+
285+
286+
287+
288+
289+
290+
291+
292+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env jruby
2+
3+
require 'propane'
4+
#
5+
# PixelFlow | Copyright (C) 2017 Thomas Diewald - www.thomasdiewald.com
6+
#
7+
# https://github.com/diwi/PixelFlow.git
8+
#
9+
# A Processing/Java library for high performance GPU-Computing.
10+
# MIT License: https://opensource.org/licenses/MIT
11+
#
12+
# Shadertoy Demo: https://www.shadertoy.com/view/ldSSzV
13+
# Shadertoy Author: https://www.shadertoy.com/user/TDM
14+
#
15+
class WetStone < Propane::App
16+
17+
load_library :pixel_flow
18+
java_import 'com.thomasdiewald.pixelflow.java.DwPixelFlow'
19+
java_import 'com.thomasdiewald.pixelflow.java.imageprocessing.DwShadertoy'
20+
attr_reader :context, :toy
21+
22+
def settings
23+
size(1280, 720, P2D)
24+
smooth(0)
25+
end
26+
27+
def setup
28+
surface.setResizable(true)
29+
@context = DwPixelFlow.new(self)
30+
context.print
31+
context.printGL
32+
@toy = DwShadertoy.new(context, data_path('wet_stone.frag'))
33+
frame_rate(60)
34+
end
35+
36+
def draw
37+
toy.set_iMouse(mouse_x, height - 1 - mouse_y, mouse_x, height - 1 - mouse_y) if mouse_pressed?
38+
toy.apply(g)
39+
title_format = 'Shadertoy Wet Stone | size: [%d, %d] frame_count: %d fps: %6.2f'
40+
surface.set_title(format(title_format, width, height, frame_count, frame_rate))
41+
end
42+
end
43+
44+
WetStone.new

0 commit comments

Comments
 (0)