-
Notifications
You must be signed in to change notification settings - Fork 5
/
Hills.js
157 lines (126 loc) · 5.11 KB
/
Hills.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import * as THREE from 'three'
import glsl from 'glslify'
import assets from '../lib/AssetManager'
import { addUniforms, customizeVertexShader, customizeFragmentShader } from '../lib/customizeShader'
import { wireUniform } from '../lib/Controls'
const SIZE = 20
const RESOLUTION = 256
const envmapKey = assets.queue({
url: 'assets/envMaps/49TH_STREET.exr',
type: 'envmap',
pmrem: true,
})
export class Hills extends THREE.Group {
constructor(webgl, options = {}) {
super(options)
this.webgl = webgl
this.options = options
this.material = new THREE.MeshStandardMaterial({
roughness: 0.2,
metalness: 1,
envMap: assets.get(envmapKey),
envMapIntensity: 0.5,
})
// TODO define this api
// subscribe(webgl.controls.hills.roughness, (value) => {
// material.roughness = vaule
// })
// which becomes
// subscribe(material, webgl.controls.hills.roughness)
// webgl.controlsObservable.hills.roughness.onChange((value) => {
// material.roughness = vaule
// })
// // which becomes
// webgl.controlsObservable.hills.roughness.onChange(material)
addUniforms(this.material, {
time: { value: 0 },
timeFixed: { value: 0 },
// powerFactor: { value: this.webgl.controls.hills.powerFactor },
// multiplicator: { value: this.webgl.controls.hills.multiplicator },
powerFactor: wireUniform(this.material, () => this.webgl.controls.hills.powerFactor),
multiplicator: wireUniform(this.material, () => this.webgl.controls.hills.multiplicator),
firstColor: wireUniform(this.material, () => this.webgl.controls.hills.firstColor),
secondColor: wireUniform(this.material, () => this.webgl.controls.hills.secondColor),
})
customizeVertexShader(this.material, {
head: glsl`
uniform float timeFixed;
out vec3 vTransformed;
#pragma glslify: noise = require(glsl-noise/simplex/3d)
#pragma glslify: hypot = require(glsl-hypot)
// http://lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts
vec3 orthogonal(vec3 v) {
return normalize(abs(v.x) > abs(v.z) ? vec3(-v.y, v.x, 0.0)
: vec3(0.0, -v.z, v.y));
}
// the function which defines the displacement
float frequency = 0.4;
float speed = 0.05;
float displace(vec3 point) {
// distance from center
float distance = hypot(point.xz);
float amplitude = pow(distance, 2.0) * 0.05;
return (noise(vec3(point.xz * frequency, timeFixed * speed)) * 0.5 + 0.5) * amplitude;
}
`,
main: glsl`
vec3 displacedPosition = position + normal * displace(position);
float offset = ${SIZE / RESOLUTION};
vec3 tangent = orthogonal(normal);
vec3 bitangent = normalize(cross(normal, tangent));
vec3 neighbour1 = position + tangent * offset;
vec3 neighbour2 = position + bitangent * offset;
vec3 displacedNeighbour1 = neighbour1 + normal * displace(neighbour1);
vec3 displacedNeighbour2 = neighbour2 + normal * displace(neighbour2);
// https://i.ya-webdesign.com/images/vector-normals-tangent-16.png
vec3 displacedTangent = displacedNeighbour1 - displacedPosition;
vec3 displacedBitangent = displacedNeighbour2 - displacedPosition;
// https://upload.wikimedia.org/wikipedia/commons/d/d2/Right_hand_rule_cross_product.svg
vec3 displacedNormal = normalize(cross(displacedTangent, displacedBitangent));
`,
transformed: glsl`
transformed = displacedPosition;
vTransformed = vec3(modelMatrix * vec4(transformed, 1.0));
`,
objectNormal: glsl`
objectNormal = displacedNormal;
`,
// like MeshNormalMaterial, but the normals are relative to world not camera
transformedNormal: glsl`
transformedNormal = mat3(modelMatrix) * objectNormal;
`,
})
customizeFragmentShader(this.material, {
head: glsl`
uniform float time;
uniform float powerFactor;
uniform float multiplicator;
uniform vec3 firstColor;
uniform vec3 secondColor;
in vec3 vTransformed;
`,
main: glsl`
vec3 cameraDirection = normalize(cameraPosition - vTransformed);
// 1 when facing the camera, 0 when perpendicular
float fresnel = dot(vNormal, cameraDirection);
float iridescence = pow(fresnel, powerFactor) * multiplicator;
// pingPong function
// alternate between two colors,
// the function looks like this /\/\/\/\/
float f = abs(1.0 - mod(iridescence + time, 2.0));
vec3 iridescentColor = mix(firstColor, secondColor, f);
`,
diffuse: glsl`
diffuse = iridescentColor;
`,
})
this.geometry = new THREE.PlaneBufferGeometry(SIZE, SIZE, RESOLUTION, RESOLUTION)
this.geometry.rotateX(-Math.PI / 2)
this.mesh = new THREE.Mesh(this.geometry, this.material)
this.add(this.mesh)
}
update(dt, time) {
this.material.uniforms.time.value += dt * this.webgl.controls.hills.speed
this.material.uniforms.timeFixed.value = time
}
}