├── .gitignore
├── LICENSE
├── README.md
├── base.css
├── index.html
├── package-lock.json
├── package.json
├── public
├── characters
│ ├── README.txt
│ ├── guard.glb
│ ├── paladin.glb
│ └── ybot.glb
├── models
│ ├── README.txt
│ └── mountain.glb
├── shaders
│ ├── bugs-lighting-model-fsh.glsl
│ ├── bugs-lighting-model-vsh.glsl
│ ├── common.glsl
│ ├── grass-lighting-model-fsh.glsl
│ ├── grass-lighting-model-vsh.glsl
│ ├── header.glsl
│ ├── lighting-model-fsh.glsl
│ ├── lighting-model-vsh.glsl
│ ├── noise.glsl
│ ├── oklab.glsl
│ ├── phong-lighting-model-fsh.glsl
│ ├── phong-lighting-model-vsh.glsl
│ ├── sky-lighting-model-fsh.glsl
│ ├── sky-lighting-model-vsh.glsl
│ ├── sky.glsl
│ ├── terrain-lighting-model-fsh.glsl
│ ├── terrain-lighting-model-vsh.glsl
│ ├── water-lighting-model-fsh.glsl
│ ├── water-lighting-model-vsh.glsl
│ ├── water-texture-fsh.glsl
│ ├── water-texture-vsh.glsl
│ ├── wind-lighting-model-fsh.glsl
│ └── wind-lighting-model-vsh.glsl
└── textures
│ ├── butterfly.png
│ ├── dust.png
│ ├── grass.png
│ ├── grid.png
│ ├── moth.png
│ ├── terrain.png
│ └── whitesquare.png
├── src
├── base
│ ├── entity-manager.js
│ ├── entity.js
│ ├── load-controller.js
│ ├── math.js
│ ├── passes.js
│ ├── render-component.js
│ ├── render-order.js
│ ├── render
│ │ ├── bugs-component.js
│ │ ├── grass-component.js
│ │ ├── light-component.js
│ │ ├── terrain-component.js
│ │ ├── water-component.js
│ │ └── wind-component.js
│ ├── three-defs.js
│ └── threejs-component.js
├── demo-builder.js
├── game
│ ├── player-entity.js
│ ├── player-input.js
│ ├── render
│ │ ├── render-sky-component.js
│ │ └── shaders.js
│ ├── spawners.js
│ └── third-person-camera.js
└── main.js
└── vite.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *node_modules/*
2 | *dist/*
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 simondevyoutube
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Quick_Grass
2 |
3 | Hi there, this is a cleaned up version of the source code for my video [How do Major Video Games Render Grass?](https://youtu.be/bp7REZBV4P4)
4 |
5 | If you're not already a subscriber, check out my channel: [SimonDev](https://www.youtube.com/channel/UCEwhtpXrg5MmwlH04ANpL8A)
6 |
7 | It's an implementation based on the GDC presentation for [Ghost of Tsushima's grass](https://www.gdcvault.com/play/1027033/Advanced-Graphics-Summit-Procedural-Grass)
8 |
9 |
10 | If you'd like to help choose the next video, consider support me on [Patreon](https://www.patreon.com/simondevyt)
11 |
12 |
13 | Lastly, this is released under the MIT license, so do whatever you want. If you do happen to use it in a project, I'd appreciate a shout-out or support, although you're under no obligation to do so.
14 |
15 | Cheers
--------------------------------------------------------------------------------
/base.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@500;900&display=swap');
2 |
3 | body {
4 | width: 100%;
5 | height: 100%;
6 | position: absolute;
7 | background: #000000;
8 | margin: 0;
9 | padding: 0;
10 | overscroll-behavior: none;
11 | }
12 |
13 | .container {
14 | width: 100%;
15 | height: 100%;
16 | position: relative;
17 | }
18 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SimonDev Grass Thing
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "package",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "homepage": "https://simondevyoutube.github.io/Quick_Grass/",
7 | "scripts": {
8 | "predeploy" : "npm run build",
9 | "deploy" : "gh-pages -d dist",
10 | "dev": "vite",
11 | "build": "vite build",
12 | "preview": "vite preview"
13 | },
14 | "devDependencies": {
15 | "gh-pages": "^6.0.0",
16 | "gltf-pipeline": "^4.1.0",
17 | "vite": "^4.4.9"
18 | },
19 | "dependencies": {
20 | "mersenne-twister": "^1.1.0",
21 | "three": "^0.156.0",
22 | "vite-plugin-solid": "^2.7.0",
23 | "vite-plugin-top-level-await": "^1.3.0",
24 | "vite-plugin-wasm": "^3.2.2"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/public/characters/README.txt:
--------------------------------------------------------------------------------
1 | Taken from https://www.mixamo.com/
--------------------------------------------------------------------------------
/public/characters/guard.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/6b56164272f213e353a4045d1439d071d5a968cd/public/characters/guard.glb
--------------------------------------------------------------------------------
/public/characters/paladin.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/6b56164272f213e353a4045d1439d071d5a968cd/public/characters/paladin.glb
--------------------------------------------------------------------------------
/public/characters/ybot.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/6b56164272f213e353a4045d1439d071d5a968cd/public/characters/ybot.glb
--------------------------------------------------------------------------------
/public/models/README.txt:
--------------------------------------------------------------------------------
1 | These mountains were made from rocks from https://quaternius.com/
--------------------------------------------------------------------------------
/public/models/mountain.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/6b56164272f213e353a4045d1439d071d5a968cd/public/models/mountain.glb
--------------------------------------------------------------------------------
/public/shaders/bugs-lighting-model-fsh.glsl:
--------------------------------------------------------------------------------
1 | #define PHONG
2 | uniform vec3 diffuse;
3 | uniform vec3 emissive;
4 | uniform vec3 specular;
5 | uniform float shininess;
6 | uniform float opacity;
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | varying vec3 vWorldNormal;
34 |
35 | uniform sampler2D bugsTexture;
36 |
37 | void main() {
38 | #include
39 | vec4 diffuseColor = vec4( diffuse, opacity );
40 |
41 | ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
42 | vec3 totalEmissiveRadiance = emissive;
43 | #include
44 | #include
45 | #include
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 | #include
54 | #include
55 | #include
56 | #include
57 | #include
58 | vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;
59 |
60 | #include
61 | #include
62 | #include
63 | #include
64 | #include
65 | #include
66 | #include
67 | }
--------------------------------------------------------------------------------
/public/shaders/bugs-lighting-model-vsh.glsl:
--------------------------------------------------------------------------------
1 |
2 |
3 | #define PHONG
4 | varying vec3 vViewPosition;
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | varying vec3 vWorldNormal;
19 |
20 | uniform vec2 bugsSize;
21 | uniform vec4 bugsParams;
22 | uniform float time;
23 |
24 | uniform sampler2D heightmap;
25 | uniform vec3 heightmapParams;
26 |
27 | attribute vec3 offset;
28 |
29 |
30 | void main() {
31 | #include
32 | #include
33 | #include
34 | // #include
35 |
36 | vec3 objectNormal = vec3(0.0, 1.0, 0.0);
37 | #ifdef USE_TANGENT
38 | vec3 objectTangent = vec3( tangent.xyz );
39 | #endif
40 |
41 | // #include
42 |
43 | vec3 transformed = vec3( position );
44 | #ifdef USE_ALPHAHASH
45 | vPosition = vec3( position );
46 | #endif
47 |
48 | vec4 bugHashVal = hash42(offset.xz);
49 |
50 | float BUG_SCALE = mix(0.35, 0.55, bugHashVal.z);
51 | transformed *= BUG_SCALE;
52 |
53 | const float FLAP_SPEED = 20.0;
54 | float flapTimeSample = time * FLAP_SPEED + bugHashVal.x * 100.0;
55 | transformed.y += mix(0.0, sin(flapTimeSample), abs(position.x)) * BUG_SCALE;
56 | transformed.x *= abs(cos(flapTimeSample));
57 |
58 | float TIME_PERIOD = 20.0;
59 | float repeatingTime = TIME_PERIOD * 0.5 - abs(mod(time, TIME_PERIOD) - TIME_PERIOD * 0.5);
60 |
61 | float height = noise11(time * 3.0 + bugHashVal.x * 100.0);
62 | // transformed.y += height * 0.5;
63 |
64 | // Loop
65 | float loopTime = time * 0.5 + bugHashVal.x * 123.23;
66 | float loopSize = 2.0;
67 | vec3 bugsOffset = vec3(sin(loopTime) * loopSize, height * 0.125, cos(loopTime) * loopSize) + offset;
68 |
69 | // Forward
70 | transformed = rotateY(-loopTime + PI / 2.0) * transformed;
71 | transformed += bugsOffset;
72 |
73 | // Center
74 | vec3 bugCenter = offset;
75 |
76 | vec3 bugsWorldPos = (modelMatrix * vec4(bugCenter, 1.0)).xyz;
77 | vec2 heightmapUV = vec2(
78 | remap(bugsWorldPos.x, -heightmapParams.z * 0.5, heightmapParams.z * 0.5, 0.0, 1.0),
79 | remap(bugsWorldPos.z, -heightmapParams.z * 0.5, heightmapParams.z * 0.5, 1.0, 0.0));
80 | float terrainHeight = texture2D(heightmap, heightmapUV).x * heightmapParams.x - heightmapParams.y;
81 | transformed.y += terrainHeight;
82 |
83 | if (terrainHeight < -11.0) {
84 | transformed.y -= 1000.0;
85 | }
86 |
87 | objectNormal = normal;
88 |
89 | #include
90 | #include
91 | #include
92 | #include
93 | #include
94 |
95 | #include
96 | #include
97 | #include
98 |
99 | // #include
100 | vec4 mvPosition = vec4( transformed, 1.0 );
101 | #ifdef USE_INSTANCING
102 | mvPosition = instanceMatrix * mvPosition;
103 | #endif
104 | mvPosition = modelViewMatrix * mvPosition;
105 | gl_Position = projectionMatrix * mvPosition;
106 |
107 | #include
108 | #include
109 | vViewPosition = - mvPosition.xyz;
110 | #include
111 | #include
112 | #include
113 | #include
114 |
115 | vWorldNormal = (modelMatrix * vec4(normal.xyz, 0.0)).xyz;
116 | }
--------------------------------------------------------------------------------
/public/shaders/common.glsl:
--------------------------------------------------------------------------------
1 | // #define PI 3.14159265359
2 |
3 |
4 | float saturate(float x) {
5 | return clamp(x, 0.0, 1.0);
6 | }
7 |
8 | vec2 saturate2(vec2 x) {
9 | return clamp(x, vec2(0.0), vec2(1.0));
10 | }
11 |
12 | vec3 saturate3(vec3 x) {
13 | return clamp(x, vec3(0.0), vec3(1.0));
14 | }
15 |
16 |
17 | float linearstep(float minValue, float maxValue, float v) {
18 | return clamp((v - minValue) / (maxValue - minValue), 0.0, 1.0);
19 | }
20 |
21 | float inverseLerp(float minValue, float maxValue, float v) {
22 | return (v - minValue) / (maxValue - minValue);
23 | }
24 |
25 | float inverseLerpSat(float minValue, float maxValue, float v) {
26 | return saturate((v - minValue) / (maxValue - minValue));
27 | }
28 |
29 | float remap(float v, float inMin, float inMax, float outMin, float outMax) {
30 | float t = inverseLerp(inMin, inMax, v);
31 | return mix(outMin, outMax, t);
32 | }
33 |
34 | vec3 LINEAR_TO_GAMMA(vec3 value) {
35 | vec3 colour = pow(value, vec3(1.0 / 2.2));
36 |
37 | return colour;
38 | }
39 |
40 | vec3 GAMMA_TO_LINEAR(vec3 value) {
41 | vec3 colour = pow(value, vec3(2.2));
42 |
43 | return colour;
44 | }
45 |
46 |
47 | float easeOut(float x, float t) {
48 | return 1.0 - pow(1.0 - x, t);
49 | }
50 |
51 | float easeIn(float x, float t) {
52 | return pow(x, t);
53 | }
54 |
55 |
56 | mat2 rotate2D(float angle) {
57 | float s = sin(angle);
58 | float c = cos(angle);
59 | return mat2(c, -s, s, c);
60 | }
61 |
62 | mat3 rotateX(float theta) {
63 | float c = cos(theta);
64 | float s = sin(theta);
65 | return mat3(
66 | vec3(1, 0, 0),
67 | vec3(0, c, -s),
68 | vec3(0, s, c)
69 | );
70 | }
71 |
72 | // Rotation matrix around the Y axis.
73 | mat3 rotateY(float theta) {
74 | float c = cos(theta);
75 | float s = sin(theta);
76 | return mat3(
77 | vec3(c, 0, s),
78 | vec3(0, 1, 0),
79 | vec3(-s, 0, c)
80 | );
81 | }
82 |
83 | // Rotation matrix around the Z axis.
84 | mat3 rotateZ(float theta) {
85 | float c = cos(theta);
86 | float s = sin(theta);
87 | return mat3(
88 | vec3(c, -s, 0),
89 | vec3(s, c, 0),
90 | vec3(0, 0, 1)
91 | );
92 | }
93 |
94 | mat3 rotateAxis(vec3 axis, float angle) {
95 | axis = normalize(axis);
96 | float s = sin(angle);
97 | float c = cos(angle);
98 | float oc = 1.0 - c;
99 |
100 | return mat3(
101 | oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s,
102 | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s,
103 | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c
104 | );
105 | }
106 |
--------------------------------------------------------------------------------
/public/shaders/grass-lighting-model-fsh.glsl:
--------------------------------------------------------------------------------
1 | #define PHONG
2 | uniform vec3 diffuse;
3 | uniform vec3 emissive;
4 | uniform vec3 specular;
5 | uniform float shininess;
6 | uniform float opacity;
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | // #include
26 |
27 | uniform sampler2D grassTexture;
28 | uniform vec3 grassLODColour;
29 | uniform float time;
30 | uniform mat3 normalMatrix;
31 |
32 | varying vec3 vGrassColour;
33 | varying vec4 vGrassParams;
34 | varying vec3 vNormal2;
35 | varying vec3 vWorldPosition;
36 |
37 | varying vec3 vViewPosition;
38 | struct BlinnPhongMaterial {
39 | vec3 diffuseColor;
40 | vec3 specularColor;
41 | float specularShininess;
42 | float specularStrength;
43 | };
44 |
45 |
46 | void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {
47 | float wrap = 0.5;
48 | float dotNL = saturate( (dot( geometry.normal, directLight.direction ) + wrap) / (1.0 + wrap) );
49 | vec3 irradiance = dotNL * directLight.color;
50 | reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );
51 | reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength;
52 |
53 | // Backscatter fakery
54 | wrap = 0.5;
55 | float backLight = saturate((dot(geometry.viewDir, -directLight.direction) + wrap) / (1.0 + wrap));
56 | float falloff = 0.5;//mix(0.5, pow(1.0 - saturate(dot(geometry.viewDir, geometry.normal)), 2.0), 1.0) * 0.5;
57 | vec3 scatter = directLight.color * pow(backLight, 1.0) * falloff * BRDF_Lambert(material.diffuseColor);
58 |
59 | reflectedLight.indirectDiffuse += scatter * (1.0 - vGrassParams.z);
60 | }
61 | void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {
62 | reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );
63 | }
64 | #define RE_Direct RE_Direct_BlinnPhong
65 | #define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong
66 |
67 | #include
68 | #include
69 | #include
70 | #include
71 | #include
72 | #include
73 |
74 |
75 | void main() {
76 | vec3 viewDir = normalize(cameraPosition - vWorldPosition);
77 |
78 | #include
79 | vec4 diffuseColor = vec4( diffuse, opacity );
80 |
81 | // Grass
82 | float heightPercent = vGrassParams.x;
83 | float height = vGrassParams.y;
84 | float lodFadeIn = vGrassParams.z;
85 | float lodFadeOut = 1.0 - lodFadeIn;
86 |
87 | float grassMiddle = mix(
88 | smoothstep(abs(vGrassParams.w - 0.5), 0.0, 0.1), 1.0, lodFadeIn);
89 |
90 | float isSandy = saturate(linearstep(-11.0, -14.0, height));
91 |
92 | float density = 1.0 - isSandy;
93 |
94 | // Density is in the range [0, 1]
95 | // 0 being no grass
96 | // 1 being full grass
97 | float aoForDensity = mix(1.0, 0.25, density);
98 | float ao = mix(aoForDensity, 1.0, easeIn(heightPercent, 2.0));
99 |
100 | diffuseColor.rgb *= vGrassColour;
101 | diffuseColor.rgb *= mix(0.85, 1.0, grassMiddle);
102 | diffuseColor.rgb *= ao;
103 |
104 |
105 | ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
106 | vec3 totalEmissiveRadiance = emissive;
107 | #include
108 | #include
109 | #include
110 | #include
111 | #include
112 | #include
113 | #include
114 | #include
115 | #include
116 |
117 | vec3 normal2 = normalize(vNormal2);
118 | normal = normalize(mix(vNormal, normal2, vGrassParams.w));
119 |
120 | #include
121 | // #include
122 |
123 | BlinnPhongMaterial material;
124 | material.diffuseColor = diffuseColor.rgb;
125 | material.specularColor = specular;
126 |
127 | #include
128 | #include
129 | #include
130 | #include
131 | vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;
132 |
133 | #include
134 | #include
135 | #include
136 | #include
137 | // #include
138 |
139 | gl_FragColor.xyz = CalculateFog(gl_FragColor.xyz, viewDir, vFogDepth);
140 |
141 | #include
142 | #include
143 | }
--------------------------------------------------------------------------------
/public/shaders/grass-lighting-model-vsh.glsl:
--------------------------------------------------------------------------------
1 |
2 |
3 | #define PHONG
4 | varying vec3 vViewPosition;
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | varying vec3 vWorldNormal;
19 | varying vec3 vGrassColour;
20 | varying vec4 vGrassParams;
21 | varying vec3 vNormal2;
22 | varying vec3 vWorldPosition;
23 |
24 | uniform vec2 grassSize;
25 | uniform vec4 grassParams;
26 | uniform vec4 grassDraw;
27 | uniform float time;
28 | uniform sampler2D heightmap;
29 | uniform vec4 heightParams;
30 | uniform vec3 playerPos;
31 | uniform mat4 viewMatrixInverse;
32 |
33 | attribute float vertIndex;
34 |
35 |
36 | void main() {
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 |
43 | vec3 grassOffset = vec3(position.x, 0.0, position.y);
44 |
45 | // Blade world position
46 | vec3 grassBladeWorldPos = (modelMatrix * vec4(grassOffset, 1.0)).xyz;
47 | vec2 heightmapUV = vec2(
48 | remap(grassBladeWorldPos.x, -heightParams.x * 0.5, heightParams.x * 0.5, 0.0, 1.0),
49 | remap(grassBladeWorldPos.z, -heightParams.x * 0.5, heightParams.x * 0.5, 1.0, 0.0));
50 | vec4 heightmapSample = texture2D(heightmap, heightmapUV);
51 | grassBladeWorldPos.y += heightmapSample.x * grassParams.z - grassParams.w;
52 |
53 | float heightmapSampleHeight = 1.0;//mix(0.5, 1.0, heightmapSample.y);
54 |
55 | vec4 hashVal1 = hash42(vec2(grassBladeWorldPos.x, grassBladeWorldPos.z));
56 |
57 | float highLODOut = smoothstep(grassDraw.x * 0.5, grassDraw.x, distance(cameraPosition, grassBladeWorldPos));
58 | float lodFadeIn = smoothstep(grassDraw.x, grassDraw.y, distance(cameraPosition, grassBladeWorldPos));
59 |
60 | // Check terrain type, maybe don't allow grass blade
61 | float isSandy = linearstep(-11.0, -14.0, grassBladeWorldPos.y);
62 | float grassAllowedHash = hashVal1.w - isSandy;
63 | float isGrassAllowed = step(0.0, grassAllowedHash);
64 |
65 | float randomAngle = hashVal1.x * 2.0 * 3.14159;
66 | float randomShade = remap(hashVal1.y, -1.0, 1.0, 0.5, 1.0);
67 | float randomHeight = remap(hashVal1.z, 0.0, 1.0, 0.75, 1.5) * mix(1.0, 0.0, lodFadeIn) * isGrassAllowed * heightmapSampleHeight;
68 | float randomWidth = (1.0 - isSandy) * heightmapSampleHeight;
69 | float randomLean = remap(hashVal1.w, 0.0, 1.0, 0.1, 0.4);
70 |
71 | vec2 hashGrassColour = hash22(vec2(grassBladeWorldPos.x, grassBladeWorldPos.z));
72 | float leanAnimation = noise12(vec2(time * 0.35) + grassBladeWorldPos.xz * 137.423) * 0.1;
73 |
74 | float GRASS_SEGMENTS = grassParams.x;
75 | float GRASS_VERTICES = grassParams.y;
76 |
77 | // Figure out vertex id, > GRASS_VERTICES is back side
78 | float vertID = mod(float(vertIndex), GRASS_VERTICES);
79 |
80 | // 1 = front, -1 = back
81 | float zSide = -(floor(vertIndex / GRASS_VERTICES) * 2.0 - 1.0);
82 |
83 | // 0 = left, 1 = right
84 | float xSide = mod(vertID, 2.0);
85 |
86 | float heightPercent = (vertID - xSide) / (GRASS_SEGMENTS * 2.0);
87 |
88 | float grassTotalHeight = grassSize.y * randomHeight;
89 | float grassTotalWidthHigh = easeOut(1.0 - heightPercent, 2.0);
90 | float grassTotalWidthLow = 1.0 - heightPercent;
91 | float grassTotalWidth = grassSize.x * mix(grassTotalWidthHigh, grassTotalWidthLow, highLODOut) * randomWidth;
92 |
93 | // Shift verts
94 | float x = (xSide - 0.5) * grassTotalWidth;
95 | float y = heightPercent * grassTotalHeight;
96 |
97 | float windDir = noise12(grassBladeWorldPos.xz * 0.05 + 0.05 * time);
98 | float windNoiseSample = noise12(grassBladeWorldPos.xz * 0.25 + time * 1.0);
99 | float windLeanAngle = remap(windNoiseSample, -1.0, 1.0, 0.25, 1.0);
100 | windLeanAngle = easeIn(windLeanAngle, 2.0) * 1.25;
101 | vec3 windAxis = vec3(cos(windDir), 0.0, sin(windDir));
102 |
103 | windLeanAngle *= heightPercent;
104 |
105 | float distToPlayer = distance(grassBladeWorldPos.xz, playerPos.xz);
106 | float playerFalloff = smoothstep(2.5, 1.0, distToPlayer);
107 | float playerLeanAngle = mix(0.0, 0.2, playerFalloff * linearstep(0.5, 0.0, windLeanAngle));
108 | vec3 grassToPlayer = normalize(vec3(playerPos.x, 0.0, playerPos.z) - vec3(grassBladeWorldPos.x, 0.0, grassBladeWorldPos.z));
109 | vec3 playerLeanAxis = vec3(grassToPlayer.z, 0, -grassToPlayer.x);
110 |
111 | randomLean += leanAnimation;
112 |
113 | float easedHeight = mix(easeIn(heightPercent, 2.0), 1.0, highLODOut);
114 | float curveAmount = -randomLean * easedHeight;
115 |
116 | float ncurve1 = -randomLean * easedHeight;
117 | vec3 n1 = vec3(0.0, (heightPercent + 0.01), 0.0);
118 | n1 = rotateX(ncurve1) * n1;
119 |
120 | float ncurve2 = -randomLean * easedHeight * 0.9;
121 | vec3 n2 = vec3(0.0, (heightPercent + 0.01) * 0.9, 0.0);
122 | n2 = rotateX(ncurve2) * n2;
123 |
124 | vec3 ncurve = normalize(n1 - n2);
125 |
126 | mat3 grassMat = rotateAxis(playerLeanAxis, playerLeanAngle) * rotateAxis(windAxis, windLeanAngle) * rotateY(randomAngle);
127 |
128 | vec3 grassFaceNormal = vec3(0.0, 0.0, 1.0);
129 | grassFaceNormal = grassMat * grassFaceNormal;
130 | grassFaceNormal *= zSide;
131 |
132 | vec3 grassVertexNormal = vec3(0.0, -ncurve.z, ncurve.y);
133 | vec3 grassVertexNormal1 = rotateY(PI * 0.3 * zSide) * grassVertexNormal;
134 | vec3 grassVertexNormal2 = rotateY(PI * -0.3 * zSide) * grassVertexNormal;
135 |
136 | grassVertexNormal1 = grassMat * grassVertexNormal1;
137 | grassVertexNormal1 *= zSide;
138 |
139 | grassVertexNormal2 = grassMat * grassVertexNormal2;
140 | grassVertexNormal2 *= zSide;
141 |
142 | vec3 grassVertexPosition = vec3(x, y, 0.0);
143 | grassVertexPosition = rotateX(curveAmount) * grassVertexPosition;
144 | grassVertexPosition = grassMat * grassVertexPosition;
145 |
146 | grassVertexPosition += grassOffset;
147 |
148 | vec3 b1 = vec3(0.02, 0.075, 0.01);
149 | vec3 b2 = vec3(0.025, 0.1, 0.01);
150 | vec3 t1 = vec3(0.65, 0.8, 0.25);
151 | vec3 t2 = vec3(0.8, 0.9, 0.4);
152 |
153 | vec3 baseColour = mix(b1, b2, hashGrassColour.x);
154 | vec3 tipColour = mix(t1, t2, hashGrassColour.y);
155 | vec3 highLODColour = mix(baseColour, tipColour, easeIn(heightPercent, 4.0)) * randomShade;
156 | vec3 lowLODColour = mix(b1, t1, heightPercent);
157 | vGrassColour = mix(highLODColour, lowLODColour, highLODOut);
158 | vGrassParams = vec4(heightPercent, grassBladeWorldPos.y, highLODOut, xSide);
159 |
160 | const float SKY_RATIO = 0.25;
161 | // TODO: Grab terrain normal
162 | vec3 UP = vec3(0.0, 1.0, 0.0);
163 | // float skyFadeIn = smoothstep(grassDraw.x * 0.5, grassDraw.x, distance(cameraPosition, grassBladeWorldPos)) * SKY_RATIO;
164 | float skyFadeIn = (1.0 - highLODOut) * SKY_RATIO;
165 | vec3 normal1 = normalize(mix(UP, grassVertexNormal1, skyFadeIn));
166 | vec3 normal2 = normalize(mix(UP, grassVertexNormal2, skyFadeIn));
167 |
168 | transformed = grassVertexPosition;
169 | transformed.y += grassBladeWorldPos.y;
170 |
171 | vec3 cameraWorldLeft = (viewMatrixInverse * vec4(-1.0, 0.0, 0.0, 0.0)).xyz;
172 |
173 | vec3 viewDir = normalize(cameraPosition - grassBladeWorldPos);
174 | vec3 viewDirXZ = normalize(vec3(viewDir.x, 0.0, viewDir.z));
175 |
176 | vec3 grassFaceNormalXZ = normalize(vec3(grassFaceNormal.x, 0.0, grassFaceNormal.z));
177 |
178 | float viewDotNormal = saturate(dot(grassFaceNormal, viewDirXZ));
179 | float viewSpaceThickenFactor = easeOut(1.0 - viewDotNormal, 4.0) * smoothstep(0.0, 0.2, viewDotNormal);
180 |
181 | objectNormal = grassVertexNormal1;
182 |
183 | #include
184 | #include
185 | #include
186 |
187 | #include
188 | #include
189 |
190 | vNormal = normalize(normalMatrix * normal1);
191 | vNormal2 = normalize(normalMatrix * normal2);
192 |
193 | #include
194 | #include
195 | #include
196 |
197 | // #include
198 | vec4 mvPosition = vec4( transformed, 1.0 );
199 | #ifdef USE_INSTANCING
200 | mvPosition = instanceMatrix * mvPosition;
201 | #endif
202 | mvPosition = modelViewMatrix * mvPosition;
203 |
204 | // HACK
205 | mvPosition.x += viewSpaceThickenFactor * (xSide - 0.5) * grassTotalWidth * 0.5 * zSide;
206 |
207 | gl_Position = projectionMatrix * mvPosition;
208 |
209 | #include
210 | #include
211 | vViewPosition = - mvPosition.xyz;
212 | #include
213 | #include
214 | #include
215 | #include
216 |
217 | vWorldPosition = worldPosition.xyz;
218 | }
--------------------------------------------------------------------------------
/public/shaders/header.glsl:
--------------------------------------------------------------------------------
1 |
2 |
3 | vec3 COLOUR_LIGHT_BLUE = vec3(0.42, 0.65, 0.85);
4 | vec3 COLOUR_LIGHT_GREEN = vec3(0.25, 1.0, 0.25);
5 | vec3 COLOUR_PALE_GREEN = vec3(0.42, 0.85, 0.65);
6 | vec3 COLOUR_LIGHT_PURPLE = vec3(0.85, 0.25, 0.85);
7 | vec3 COLOUR_BRIGHT_PINK = vec3(1.0, 0.5, 0.5);
8 |
9 | vec3 COLOUR_BRIGHT_RED = vec3(1.0, 0.1, 0.02);
10 | vec3 COLOUR_BRIGHT_BLUE = vec3(0.01, 0.2, 1.0);
11 | vec3 COLOUR_BRIGHT_GREEN = vec3(0.01, 1.0, 0.2);
12 | vec3 COLOUR_PALE_BLUE = vec3(0.42, 0.65, 0.85);
13 | vec3 COLOUR_LIGHT_YELLOW = vec3(1.0, 1.0, 0.25);
14 |
15 |
16 |
17 | #define USE_OKLAB
18 |
--------------------------------------------------------------------------------
/public/shaders/lighting-model-fsh.glsl:
--------------------------------------------------------------------------------
1 | #define STANDARD
2 | #ifdef PHYSICAL
3 | #define IOR
4 | #define USE_SPECULAR
5 | #endif
6 | uniform vec3 diffuse;
7 | uniform vec3 emissive;
8 | uniform float roughness;
9 | uniform float metalness;
10 | uniform float opacity;
11 | #ifdef IOR
12 | uniform float ior;
13 | #endif
14 | #ifdef USE_SPECULAR
15 | uniform float specularIntensity;
16 | uniform vec3 specularColor;
17 | #ifdef USE_SPECULAR_COLORMAP
18 | uniform sampler2D specularColorMap;
19 | #endif
20 | #ifdef USE_SPECULAR_INTENSITYMAP
21 | uniform sampler2D specularIntensityMap;
22 | #endif
23 | #endif
24 | #ifdef USE_CLEARCOAT
25 | uniform float clearcoat;
26 | uniform float clearcoatRoughness;
27 | #endif
28 | #ifdef USE_IRIDESCENCE
29 | uniform float iridescence;
30 | uniform float iridescenceIOR;
31 | uniform float iridescenceThicknessMinimum;
32 | uniform float iridescenceThicknessMaximum;
33 | #endif
34 | #ifdef USE_SHEEN
35 | uniform vec3 sheenColor;
36 | uniform float sheenRoughness;
37 | #ifdef USE_SHEEN_COLORMAP
38 | uniform sampler2D sheenColorMap;
39 | #endif
40 | #ifdef USE_SHEEN_ROUGHNESSMAP
41 | uniform sampler2D sheenRoughnessMap;
42 | #endif
43 | #endif
44 | #ifdef USE_ANISOTROPY
45 | uniform vec2 anisotropyVector;
46 | #ifdef USE_ANISOTROPYMAP
47 | uniform sampler2D anisotropyMap;
48 | #endif
49 | #endif
50 | varying vec3 vViewPosition;
51 | #include
52 | #include
53 | #include
54 | #include
55 | #include
56 | #include
57 | #include
58 | #include
59 | #include
60 | #include
61 | #include
62 | #include
63 | #include
64 | #include
65 | #include
66 | #include
67 | #include
68 | #include
69 | #include
70 | #include
71 | #include
72 | #include
73 | #include
74 | #include
75 | #include
76 | #include
77 | #include
78 | #include
79 | #include
80 | #include
81 |
82 | varying vec3 vWorldNormal;
83 |
84 | void main() {
85 | #include
86 | vec4 diffuseColor = vec4( diffuse, opacity );
87 | ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
88 | vec3 totalEmissiveRadiance = emissive;
89 | #include
90 | #include
91 | #include
92 | #include
93 | #include
94 | #include
95 | #include
96 | #include
97 | #include
98 | #include
99 | #include
100 | #include
101 | #include
102 | #include
103 | #include
104 | #include
105 | #include
106 | #include
107 | vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;
108 | vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;
109 | #include
110 | vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;
111 | #ifdef USE_SHEEN
112 | float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );
113 | outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;
114 | #endif
115 | #ifdef USE_CLEARCOAT
116 | float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );
117 | vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );
118 | outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;
119 | #endif
120 |
121 | #include
122 | #include
123 | #include
124 | #include
125 | #include
126 | #include
127 | }
128 |
--------------------------------------------------------------------------------
/public/shaders/lighting-model-vsh.glsl:
--------------------------------------------------------------------------------
1 | #define STANDARD
2 | varying vec3 vViewPosition;
3 | #ifdef USE_TRANSMISSION
4 | varying vec3 vWorldPosition;
5 | #endif
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | varying vec3 vWorldNormal;
19 |
20 | void main() {
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | vViewPosition = - mvPosition.xyz;
39 |
40 | // #include
41 | #if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0
42 | vec4 worldPosition = vec4( transformed, 1.0 );
43 | #ifdef USE_INSTANCING
44 | worldPosition = instanceMatrix * worldPosition;
45 | #endif
46 | worldPosition = modelMatrix * worldPosition;
47 | #endif
48 |
49 | #include
50 | #include
51 | #ifdef USE_TRANSMISSION
52 | vWorldPosition = worldPosition.xyz;
53 | #endif
54 |
55 | vWorldNormal = (modelMatrix * vec4(normal.xyz, 0.0)).xyz;
56 | // vWorldNormal = normal.xyz;
57 | }
--------------------------------------------------------------------------------
/public/shaders/noise.glsl:
--------------------------------------------------------------------------------
1 | // Virtually all of these were taken from: https://www.shadertoy.com/view/ttc3zr
2 |
3 | uvec4 murmurHash42(uvec2 src) {
4 | const uint M = 0x5bd1e995u;
5 | uvec4 h = uvec4(1190494759u, 2147483647u, 3559788179u, 179424673u);
6 | src *= M; src ^= src>>24u; src *= M;
7 | h *= M; h ^= src.x; h *= M; h ^= src.y;
8 | h ^= h>>13u; h *= M; h ^= h>>15u;
9 | return h;
10 | }
11 |
12 | uint murmurHash11(uint src) {
13 | const uint M = 0x5bd1e995u;
14 | uint h = 1190494759u;
15 | src *= M; src ^= src>>24u; src *= M;
16 | h *= M; h ^= src;
17 | h ^= h>>13u; h *= M; h ^= h>>15u;
18 | return h;
19 | }
20 |
21 | uint murmurHash12(uvec2 src) {
22 | const uint M = 0x5bd1e995u;
23 | uint h = 1190494759u;
24 | src *= M; src ^= src>>24u; src *= M;
25 | h *= M; h ^= src.x; h *= M; h ^= src.y;
26 | h ^= h>>13u; h *= M; h ^= h>>15u;
27 | return h;
28 | }
29 |
30 | uint murmurHash13(uvec3 src) {
31 | const uint M = 0x5bd1e995u;
32 | uint h = 1190494759u;
33 | src *= M; src ^= src>>24u; src *= M;
34 | h *= M; h ^= src.x; h *= M; h ^= src.y; h *= M; h ^= src.z;
35 | h ^= h>>13u; h *= M; h ^= h>>15u;
36 | return h;
37 | }
38 |
39 | uvec2 murmurHash22(uvec2 src) {
40 | const uint M = 0x5bd1e995u;
41 | uvec2 h = uvec2(1190494759u, 2147483647u);
42 | src *= M; src ^= src>>24u; src *= M;
43 | h *= M; h ^= src.x; h *= M; h ^= src.y;
44 | h ^= h>>13u; h *= M; h ^= h>>15u;
45 | return h;
46 | }
47 |
48 | uvec2 murmurHash21(uint src) {
49 | const uint M = 0x5bd1e995u;
50 | uvec2 h = uvec2(1190494759u, 2147483647u);
51 | src *= M; src ^= src>>24u; src *= M;
52 | h *= M; h ^= src;
53 | h ^= h>>13u; h *= M; h ^= h>>15u;
54 | return h;
55 | }
56 |
57 | uvec2 murmurHash23(uvec3 src) {
58 | const uint M = 0x5bd1e995u;
59 | uvec2 h = uvec2(1190494759u, 2147483647u);
60 | src *= M; src ^= src>>24u; src *= M;
61 | h *= M; h ^= src.x; h *= M; h ^= src.y; h *= M; h ^= src.z;
62 | h ^= h>>13u; h *= M; h ^= h>>15u;
63 | return h;
64 | }
65 |
66 | uvec3 murmurHash31(uint src) {
67 | const uint M = 0x5bd1e995u;
68 | uvec3 h = uvec3(1190494759u, 2147483647u, 3559788179u);
69 | src *= M; src ^= src>>24u; src *= M;
70 | h *= M; h ^= src;
71 | h ^= h>>13u; h *= M; h ^= h>>15u;
72 | return h;
73 | }
74 |
75 | uvec3 murmurHash33(uvec3 src) {
76 | const uint M = 0x5bd1e995u;
77 | uvec3 h = uvec3(1190494759u, 2147483647u, 3559788179u);
78 | src *= M; src ^= src>>24u; src *= M;
79 | h *= M; h ^= src.x; h *= M; h ^= src.y; h *= M; h ^= src.z;
80 | h ^= h>>13u; h *= M; h ^= h>>15u;
81 | return h;
82 | }
83 |
84 | // 3 outputs, 3 inputs
85 | vec3 hash33(vec3 src) {
86 | uvec3 h = murmurHash33(floatBitsToUint(src));
87 | return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
88 | }
89 |
90 | // 1 output, 1 input
91 | float hash11(float src) {
92 | uint h = murmurHash11(floatBitsToUint(src));
93 | return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
94 | }
95 |
96 | // 1 output, 2 inputs
97 | float hash12(vec2 src) {
98 | uint h = murmurHash12(floatBitsToUint(src));
99 | return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
100 | }
101 |
102 | // 1 output, 3 inputs
103 | float hash13(vec3 src) {
104 | uint h = murmurHash13(floatBitsToUint(src));
105 | return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
106 | }
107 |
108 | // 2 outputs, 1 input
109 | vec2 hash21(float src) {
110 | uvec2 h = murmurHash21(floatBitsToUint(src));
111 | return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
112 | }
113 |
114 | // 3 outputs, 1 input
115 | vec3 hash31(float src) {
116 | uvec3 h = murmurHash31(floatBitsToUint(src));
117 | return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
118 | }
119 |
120 | // 2 outputs, 2 inputs
121 | vec2 hash22(vec2 src) {
122 | uvec2 h = murmurHash22(floatBitsToUint(src));
123 | return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
124 | }
125 |
126 | // 4 outputs, 2 inputs
127 | vec4 hash42(vec2 src) {
128 | uvec4 h = murmurHash42(floatBitsToUint(src));
129 | return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
130 | }
131 |
132 |
133 | // 2 outputs, 3 inputs
134 | vec2 hash23(vec3 src) {
135 | uvec2 h = murmurHash23(floatBitsToUint(src));
136 | return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
137 | }
138 |
139 | float noise11(float p) {
140 | float i = floor(p);
141 |
142 | float f = fract(p);
143 | float u = smoothstep(0.0, 1.0, f);
144 |
145 | float val = mix( hash11(i + 0.0),
146 | hash11(i + 1.0), u);
147 | return val * 2.0 - 1.0;
148 | }
149 |
150 | float noise12(vec2 p) {
151 | vec2 i = floor(p);
152 |
153 | vec2 f = fract(p);
154 | vec2 u = smoothstep(vec2(0.0), vec2(1.0), f);
155 |
156 | float val = mix( mix( hash12( i + vec2(0.0, 0.0) ),
157 | hash12( i + vec2(1.0, 0.0) ), u.x),
158 | mix( hash12( i + vec2(0.0, 1.0) ),
159 | hash12( i + vec2(1.0, 1.0) ), u.x), u.y);
160 | return val * 2.0 - 1.0;
161 | }
162 |
163 | float noise13(vec3 x) {
164 | vec3 i = floor(x);
165 | vec3 f = fract(x);
166 | f = f*f*(3.0-2.0*f);
167 |
168 | return mix(mix(mix( hash13(i+vec3(0.0, 0.0, 0.0)),
169 | hash13(i+vec3(1.0, 0.0, 0.0)),f.x),
170 | mix( hash13(i+vec3(0.0, 1.0, 0.0)),
171 | hash13(i+vec3(1.0, 1.0, 0.0)),f.x),f.y),
172 | mix(mix( hash13(i+vec3(0.0, 0.0, 1.0)),
173 | hash13(i+vec3(1.0, 0.0, 1.0)),f.x),
174 | mix( hash13(i+vec3(0.0, 1.0, 1.0)),
175 | hash13(i+vec3(1.0, 1.0, 1.0)),f.x),f.y),f.z);
176 | }
177 |
178 | vec2 noise23(vec3 x) {
179 | vec3 i = floor(x);
180 | vec3 f = fract(x);
181 | f = f*f*(3.0-2.0*f);
182 |
183 | return mix(mix(mix( hash23(i+vec3(0.0, 0.0, 0.0)),
184 | hash23(i+vec3(1.0, 0.0, 0.0)),f.x),
185 | mix( hash23(i+vec3(0.0, 1.0, 0.0)),
186 | hash23(i+vec3(1.0, 1.0, 0.0)),f.x),f.y),
187 | mix(mix( hash23(i+vec3(0.0, 0.0, 1.0)),
188 | hash23(i+vec3(1.0, 0.0, 1.0)),f.x),
189 | mix( hash23(i+vec3(0.0, 1.0, 1.0)),
190 | hash23(i+vec3(1.0, 1.0, 1.0)),f.x),f.y),f.z);
191 | }
192 |
193 | vec2 noise22(vec2 p) {
194 | vec2 i = floor(p);
195 |
196 | vec2 f = fract(p);
197 | vec2 u = smoothstep(vec2(0.0), vec2(1.0), f);
198 |
199 | vec2 val = mix( mix( hash22( i + vec2(0.0, 0.0) ),
200 | hash22( i + vec2(1.0, 0.0) ), u.x),
201 | mix( hash22( i + vec2(0.0, 1.0) ),
202 | hash22( i + vec2(1.0, 1.0) ), u.x), u.y);
203 | return val * 2.0 - 1.0;
204 | }
205 |
206 | // The MIT License
207 | // Copyright © 2017 Inigo Quilez
208 | // 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.
209 | // https://www.youtube.com/c/InigoQuilez
210 | // https://iquilezles.org/
211 | vec4 noised_1_3(vec3 x) {
212 | vec3 i = floor(x);
213 | vec3 f = fract(x);
214 |
215 | // quintic interpolant
216 | vec3 u = f*f*f*(f*(f*6.0-15.0)+10.0);
217 | vec3 du = 30.0*f*f*(f*(f-2.0)+1.0);
218 |
219 | // gradients
220 | vec3 ga = hash33( i+vec3(0.0,0.0,0.0) ) * 2.0 - 1.0;
221 | vec3 gb = hash33( i+vec3(1.0,0.0,0.0) ) * 2.0 - 1.0;
222 | vec3 gc = hash33( i+vec3(0.0,1.0,0.0) ) * 2.0 - 1.0;
223 | vec3 gd = hash33( i+vec3(1.0,1.0,0.0) ) * 2.0 - 1.0;
224 | vec3 ge = hash33( i+vec3(0.0,0.0,1.0) ) * 2.0 - 1.0;
225 | vec3 gf = hash33( i+vec3(1.0,0.0,1.0) ) * 2.0 - 1.0;
226 | vec3 gg = hash33( i+vec3(0.0,1.0,1.0) ) * 2.0 - 1.0;
227 | vec3 gh = hash33( i+vec3(1.0,1.0,1.0) ) * 2.0 - 1.0;
228 |
229 | // projections
230 | float va = dot( ga, f-vec3(0.0,0.0,0.0) );
231 | float vb = dot( gb, f-vec3(1.0,0.0,0.0) );
232 | float vc = dot( gc, f-vec3(0.0,1.0,0.0) );
233 | float vd = dot( gd, f-vec3(1.0,1.0,0.0) );
234 | float ve = dot( ge, f-vec3(0.0,0.0,1.0) );
235 | float vf = dot( gf, f-vec3(1.0,0.0,1.0) );
236 | float vg = dot( gg, f-vec3(0.0,1.0,1.0) );
237 | float vh = dot( gh, f-vec3(1.0,1.0,1.0) );
238 |
239 | // interpolations
240 | return vec4( va + u.x*(vb-va) + u.y*(vc-va) + u.z*(ve-va) + u.x*u.y*(va-vb-vc+vd) + u.y*u.z*(va-vc-ve+vg) + u.z*u.x*(va-vb-ve+vf) + (-va+vb+vc-vd+ve-vf-vg+vh)*u.x*u.y*u.z, // value
241 | ga + u.x*(gb-ga) + u.y*(gc-ga) + u.z*(ge-ga) + u.x*u.y*(ga-gb-gc+gd) + u.y*u.z*(ga-gc-ge+gg) + u.z*u.x*(ga-gb-ge+gf) + (-ga+gb+gc-gd+ge-gf-gg+gh)*u.x*u.y*u.z + // derivatives
242 | du * (vec3(vb,vc,ve) - va + u.yzx*vec3(va-vb-vc+vd,va-vc-ve+vg,va-vb-ve+vf) + u.zxy*vec3(va-vb-ve+vf,va-vb-vc+vd,va-vc-ve+vg) + u.yzx*u.zxy*(-va+vb+vc-vd+ve-vf-vg+vh) ));
243 | }
244 |
245 | float FBM_1_2(vec2 p, int octaves, float persistence, float lacunarity) {
246 | float amplitude = 1.0;
247 | float frequency = 1.0;
248 | float total = 0.0;
249 | float normalization = 0.0;
250 |
251 | for (int i = 0; i < octaves; ++i) {
252 | float noiseValue = noise12(p * frequency);
253 | total += noiseValue * amplitude;
254 | normalization += amplitude;
255 | amplitude *= persistence;
256 | frequency *= lacunarity;
257 | }
258 |
259 | total /= normalization;
260 | total = total * 0.5 + 0.5;
261 |
262 | return total;
263 | }
264 |
265 | float FBM_1_3(vec3 p, int octaves, float persistence, float lacunarity) {
266 | float amplitude = 1.0;
267 | float frequency = 1.0;
268 | float total = 0.0;
269 | float normalization = 0.0;
270 |
271 | for (int i = 0; i < octaves; ++i) {
272 | float noiseValue = noise13(p * frequency);
273 | total += noiseValue * amplitude;
274 | normalization += amplitude;
275 | amplitude *= persistence;
276 | frequency *= lacunarity;
277 | }
278 |
279 | total /= normalization;
280 | total = total * 0.5 + 0.5;
281 |
282 | return total;
283 | }
284 |
285 | const mat3 m3 = mat3( 0.00, 0.80, 0.60,
286 | -0.80, 0.36, -0.48,
287 | -0.60, -0.48, 0.64 );
288 | const mat3 m3i = mat3( 0.00, -0.80, -0.60,
289 | 0.80, 0.36, -0.48,
290 | 0.60, -0.48, 0.64 );
291 |
292 | vec4 FBM_D_1_4(in vec3 x, int octaves) {
293 | float f = 1.98; // could be 2.0
294 | float s = 0.49; // could be 0.5
295 | float a = 0.0;
296 | float b = 0.5;
297 | vec3 d = vec3(0.0);
298 | mat3 m = mat3(
299 | 1.0,0.0,0.0,
300 | 0.0,1.0,0.0,
301 | 0.0,0.0,1.0);
302 | for( int i=0; i < octaves; i++ )
303 | {
304 | vec4 n = noised_1_3(x);
305 | a += b*n.x; // accumulate values
306 | d += b*m*n.yzw; // accumulate derivatives
307 | b *= s;
308 | x = f*m3*x;
309 | m = f*m3i*m;
310 | }
311 | return vec4( a, d );
312 | }
313 |
--------------------------------------------------------------------------------
/public/shaders/oklab.glsl:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////////////////////
2 | //
3 | // OKLab stuff, mostly based off https://www.shadertoy.com/view/ttcyRS
4 | //
5 | /////////////////////////////////////////////////////////////////////////
6 |
7 | const mat3 kLMStoCONE = mat3(
8 | 4.0767245293, -1.2681437731, -0.0041119885,
9 | -3.3072168827, 2.6093323231, -0.7034763098,
10 | 0.2307590544, -0.3411344290, 1.7068625689);
11 |
12 | const mat3 kCONEtoLMS = mat3(
13 | 0.4121656120, 0.2118591070, 0.0883097947,
14 | 0.5362752080, 0.6807189584, 0.2818474174,
15 | 0.0514575653, 0.1074065790, 0.6302613616);
16 |
17 | vec3 rgbToOklab(vec3 c) {
18 | vec3 lms = kCONEtoLMS * c;
19 |
20 | return sign(lms)*pow(abs(lms), vec3(0.3333333333333));
21 | }
22 |
23 | vec3 oklabToRGB(vec3 c) {
24 | vec3 lms = c;
25 |
26 | return kLMStoCONE * (lms * lms * lms);
27 | }
28 |
29 |
30 | #ifndef USE_OKLAB
31 | #define col3 vec3
32 | #else
33 | vec3 col3(float r, float g, float b) {
34 | return rgbToOklab(vec3(r, g, b));
35 | }
36 |
37 | vec3 col3(vec3 v) {
38 | return rgbToOklab(v);
39 | }
40 |
41 | vec3 col3(float v) {
42 | return rgbToOklab(vec3(v));
43 | }
44 | #endif
--------------------------------------------------------------------------------
/public/shaders/phong-lighting-model-fsh.glsl:
--------------------------------------------------------------------------------
1 | #define PHONG
2 | uniform vec3 diffuse;
3 | uniform vec3 emissive;
4 | uniform vec3 specular;
5 | uniform float shininess;
6 | uniform float opacity;
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include