import {shadow} from './chunks/shadow.chunk'
/**Glsl sub common configuration.
* @enum {string} */
export const glConfig = {
// diffuseWeight: 0.5,
/** Ambient reflection coefficient, default '0.3' */
ka: '(0.3)',
/** Diffuse reflection coefficient, default '1.0' */
kd: '(1.0)',
/** Specular reflection coefficient, default '0.8' */
ks: '(0.8)',
/**Weight amplifier for line resterization */
lineWeight: '(0.01)',
/** * @deprecated
* default is parallel light, to change to point light, set as:
* normalize(lightpos - worldpos.xyz)
*/
dirLight: 'normalize(lightpos)', //
shadowLDR: (sname) => {
return typeof sname === 'string' ? `( 1. - 0.5 * ${sname} )` : '(1.)';
}
}
export
/**Glsl sub functions
* @enum {string} glx */
const glx = {
/**<p>uniform float u_alpha</p>
* Used for keep the name consists.
*/
u_alpha: 'uniform float u_alpha;',
/**<p>uniform float whiteAlpha</p>
* Used for keep the name consists.
*/
u_whiteAlpha: 'uniform float whiteAlpha;',
/**Is the bit is true?<br/>
* At least better than this:<br/>
* a = ...5, bit = 2 (start at 0), a_rem = a % 2^3 = 5, div = 5 / 4 >= 1
*
* Reference: <a href='https://www.khronos.org/files/webgl/webgl-reference-card-1_0.pdf'>
* WebGL 1.0 API Quick Reference Card</a>
*/
bitTrue: `
bool bitTrue(int a, int bit) {
float a_rem = mod(float(a), exp2(float(bit+1)));
return a_rem / exp2(float(bit)) >= 1.;
}`,
/**Get repeating uv for texture sampling.
*
* TODO doc: debug notes: uv must be continuous<br>
* https://community.khronos.org/t/texture-wrapping-in-shader-mipmapping/53799</br>
* https://www.shadertoy.com/view/4t2yRD</br>
* https://iquilezles.org/www/articles/tunnel/tunnel.htm</br>
*/
fractuv: `vec2 fractuv(vec2 uv) {
vec2 fuv = mod(uv, 2.);
if (fuv.s <= 1.)
fuv.s = fuv.s;
else
fuv.s = 2. - fuv.s;
if (fuv.t <= 1.)
fuv.t = fuv.t;
else
fuv.t = 2. - fuv.t;
return fuv;
}`,
/**Change intensity to mix argument t = :
* 1 - 1/(2 * intense), when x > 1
* 1/2 * x, when 0 <= x <= 1
* 0, when x < 0*/
intenseAlpha: `float intenseAlpha(float intensity) {
if (intensity > 1.) return 1. - 0.5 / intensity;
else if (intensity >= 0.) return 0.5 * intensity;
else return 0.;
}`,
/**<p>Get alpha according to eye and normal angle, can be applied to texture.</p>
* function: float fresnelAlpha(vec3 e, vec3 P, vec3 np)<br>
* param:<br>
* e: eye
* P: position
* np: normal
* return:<br>
* alpha value like of <a href='https://en.wikipedia.org/wiki/Fresnel_equations'>
* fresnel effect</a>.
*/
fresnelAlpha: `float fresnelAlpha(vec3 e, vec3 P, vec3 np) {
vec3 i = normalize(e - P);
float a = dot( i, normalize(np) );
return a > 0. ? 1. - a : 0.;
} `,
/**Find distance to ellipsoid */
sdEllipsoid: `vec2 sdEllipsoid( vec3 eye, vec3 u, float r, vec3 centr, vec3 abc ) {
// e = o - c, where o = eye, c = center
vec3 e = eye - centr;
e = e / abc;
// delta = (u . e)^2 + r^2 - |e|^2
u = normalize(u / abc);
float delta = pow( dot( u, e ), 2. ) + pow( r, 2. ) - dot(e, e);
if (delta < 0.) return vec2(delta);
// d = - u.e +/- delta^0.5
delta = pow( delta, 0.5 );
return vec2( -dot( u, e ) + delta, -dot( u, e ) - delta );
}`,
/** Mix 3 colors of interposition t */
thermalColor: `vec4 thermalColor(vec3 colrs[3], vec2 t0_1, float t) {
float midrange = abs(t0_1.y - t0_1.x) / 2.;
vec3 c0 = colrs[0];
vec3 c1 = colrs[1];
if (t > midrange) {
c0 = colrs[1]; c1 = colrs[2];
t = t - midrange;
}
return clamp(vec4(mix(c0, c1, t / abs(t0_1.y - midrange)), 1.), 0., 1.);
}`,
/**<h6>Fragment Function.</h6>
* @deprecated
* Get color on module face, reducing on distance, and is perspective on face,
* not used for volumetric rendering.<br>
* function: float pointFace( vec3 p, vec3 c )<br>
* parameters:<br>
* p: varying from vertex position, transformed to world:<pre>
vec4 v4 = modelMatrix * vec4( vec3(-150., 0., -50.), 1.0 );
P0 = v4.xyz;</pre>
* c: the point in world, e.g. varying c<pre>
* c = modelMatrix * vec4(0.);</pre>
* return {float}: distance
*/
pointFace: `float pointFace( vec3 p, vec3 c ) {
return length(p - c);
} `,
/**<h6>Fragment Function.</h6>
* 2D round box distance<br>
* parameters:<br>
* function: float box2(vec2 p, vec2 size, float r)<br>
* p {vec2}: varying from vertex poisition, transformed to world<br>
* size {vec2}: box size<br>
* r {float}: corner radius<br>
* return {float}: distance
*/
box2: `float box2(vec2 p, vec2 size, float r){
return length(max(abs(p) - size, 0.0)) - r;
}`,
/**<h6>Fragment Function.</h6>
* 3D round box distance<br>
* parameters:<br>
* function: float box2(vec2 p, vec2 size, float r)<br>
* p {vec3}: varying from vertex poisition, transformed to world<br>
* size {vec3}: box size<br>
* r {float}: corner radius<br>
* return {float}: distance
*/
box3: `float box3(vec3 p, vec3 size, float r){
return length(max(abs(p) - size, 0.0)) - r;
} `,
/**Line Rasterize Function.
* https://math.stackexchange.com/questions/2213165/find-shortest-distance-between-lines-in-3d
* 𝐧 = 𝐞1 × 𝐞2 = (−20, −11, −26)
* return {float}: distance
*/
line: `float line(vec3 e, vec3 P, vec3 p0, vec3 p1, float w) {
vec3 e2 = p1 - p0;
vec3 e1 = P - e;
vec3 n = normalize(cross(e1, e2));
float dist = dot(n, e - p0);
dist = 1.0/dist * w * ${glConfig.lineWeight};
return min(dist * dist, 1.0);
}`,
/**Rotate in 2D for angle of radian.
* function: vec2 rotate2(float radi, vec2 v)
* parameters:<br>
* radi {float} angle in radian
* v {vec2} vectore to be rotated
* return m * v. where
* m = c s
* -s c
*/
rotate2: `vec2 rotate2(float radi, vec2 v) {
float s = sin(radi);
float c = cos(radi);
return vec2(
c * v.x + s * v.y,
-s* v.x + c * v.y );
}`,
rotateY: `vec3 rotateY(float radi, vec3 v) {
float s = sin(radi);
float c = cos(radi);
return vec3(
c * v.x + s * v.z,
v.y,
-s* v.x + c * v.z );
}`,
/**
* Ray plane intersection.
* function: vec4 rayPlaneInsec(vec3 l0, vec3 l, vec3 p0, vec3 n)
* return vec4 (pos, distance), distance > 0 iff there is one point.
* Reference: <a href='https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Algebraic_form'>
* wikipedia</a>
*/
rayPlaneInsec: `vec4 rayPlaneInsec(vec3 l0, vec3 l, vec3 p0, vec3 n) {
float d = dot( (p0 - l0), n );
float l_n = dot(l, n);
if (l_n == 0.) {
if (dot(p0 - l0, n) == 0.)
return vec4(l0, 0.); // in plane
else return vec4(p0, -1.); // parallel
}
d /= l_n;
vec3 p_ = l0 + normalize(l) * d;
return vec4(p_, abs(d));
}`,
/**Get color weight according to xy, yz, xz box distance.
* Thes function depends on glx.rotateY, glx.rayPlaneInsec, glx.box3
* return colore weight
*/
box3Color: `float box3Color(vec3 e, vec3 i, vec3 c0, vec3 n0, float radi, vec3 size, vec2 tiles, float w) {
n0 = rotateY(-radi, n0);
vec4 p0 = rayPlaneInsec( e, i, c0, n0 );
if (p0.w > 0.) {
vec3 p_ = p0.xyz - c0;
vec3 p_0 = rotateY(radi, p_);
float box = box3( p_0, size * 0.5, 0.5 );
box = 1.0/box * w * (1. - va);
float tes = 0.02 * (1. - va) * ( 1. - abs( sin(now * 0.0005) ) );
return max(box, 0.05) * tes + abs(box) * 0.004;
}
else return 0.;
}`,
/**Get phong light.<br>
* sub function lambershine():<br>
* Lambert's cosine law<br>
* return: vec2(lambertian, specular)<br>
* To use in phong:<pre>
vColor = vec4(Ka * ambientColor +
Kd * lambertian * diffuseColor +
Ks * specular * specular, u_alpha);
</pre>
* Code Comments:<br>
* lambertian: angle between normal and incident<br>
* R: reflect direction<br>
* V: vector to viewer<br>
*
* <h6>External Link</h6>
* <a href='file:///home/ody/git/x-visual/docs/design-memo/shaders/phong.html'>
* x-visual doc: Morphing Phong Material</a><br>
* <a href='http://www.cs.toronto.edu/~jacobson/phong-demo/'>
* referencing implementation @ cs.toronto.edu</a><br>
* <a href='https://www.rp-photonics.com/lambertian_emitters_and_scatterers.html'>
* Lambertian Emitters and Scatters</a>
*/
phongLight: `vec2 lambershine(vec3 n, vec3 lightpos, vec3 e, vec3 vertpos, float shininess) {
vec3 L = normalize(lightpos - vertpos);
vec3 N = normalize(n);
float lambertian = max(dot(N, L), 0.0);
vec3 R = reflect(-L, N);
float specAngle = max(dot(R, normalize(e - vertpos)), 0.0);
float specular = pow(specAngle, shininess);
return vec2(lambertian, specular);
}
vec4 phongLight(vec3 n3, vec3 lightpos, vec3 eye, vec3 vertpos,
vec3 ambient, vec3 diffuse, vec3 specular, float shininess) {
float Ka = ${glConfig.ka};
float Kd = ${glConfig.kd};
float Ks = ${glConfig.ks};
vec2 lambshine = lambershine(n3, lightpos, eye, vertpos, shininess);
return vec4(Ka * ambient
+ Kd * lambshine.s * diffuse
+ Ks * lambshine.t * specular
, u_alpha);
} `,
/**<h6>Vertex Shader</h6>
* Get building wall texture's alpha - used for transparent building like glass
* cube, without refrection.
*/
buildingAlpha: `float buildingAlpha(vec3 e, vec3 P, vec3 np) {
vec3 i = normalize(e - P);
float a = dot( i, normalize(np) );
return a > 0. ? 1. - a : 0.;
}`,
shadow: {
/** use this in vertex like:<pre>
uniform mat4 directionalShadowMatrix[ ${n_light} ];
setShadowCoords(directionalShadowMatrix, worldPosition);</pre>
* Don't change name of directionalShadowMatrix, it's set by Three.js lights.
*/
setShadowCoords: shadow.v.setShadowCoords,
frag: [ shadow.f.uni_varys, shadow.f.unpackRGBAToDepth,
shadow.f.texture2DCompare, shadow.f.getShadow, shadow.f.shadow
].join('\n') },
}
/**
* Error thrown by shader/*.glsl.
* @param {number} err error message
* @param {number} [code] error code
* @class GlxError
*/
export function GlxError(err, code) {
this.code = code;
this.message = err;
this.name = 'GlxError';
}
/** A common functionf for initialize phong uniforms. Supposed to be changed in
* the future - phong uniforms should not very common for different shader?
* <p><b>Don't use this directly. This is only a shortcut of certain shaders.</b></p>
* @param {object=} uniforms if undefined, will create one
* @param {object} light
* @param {paras=} paras usually Visual.paras
* @return {object} uniforms
* @member xglsl.initPhongUni
* @function
*/
export function initPhongUni(uniforms = {}, light, paras = {}) {
uniforms.u_shininess = { value: paras.shininess ||
(uniforms.u_shininess ? uniforms.u_shininess.value : 1) };
uniforms.u_specularColor = { value: paras.shineColor ?
new THREE.Vector3(...paras.shineColor) :
new THREE.Vector3(1., 1., 1.) };
uniforms.u_ambientColor = { value: new THREE.Vector3( ...
(light.ambient || [0, 0, 0]) ) };
uniforms.u_lightPos = { value: new THREE.Vector3(...(light.position || [1, 1, 1]))};
uniforms.u_lightIntensity = { value: light.intensity === undefined ?
1 : light.intensity };
uniforms.u_color = {value: new THREE.Vector4(...light.diffuse)};
console.log('u_color', uniforms.u_color);
return uniforms;
}
/** A common functionf for undating phong uniforms. Supposed to be changed in the
* future - phong uniforms should not very common for different shader?
* <p><b>Don't use this directly. This is only a shortcut of certain shaders.</b></p>
* @param {object} uniforms uniforms to updated.
* @param {object=} light
* @param {paras=} paras usually Visual.paras
* @return {object} uniforms
* @member xglsl.updatePhongUni
* @function
*/
export function updatePhongUni(uniforms, light, paras = {}) {
if (!uniforms)
// nothing to bu updated
return;
if (typeof paras.shininess === "number") {
uniforms.u_shininess.value = paras.shininess;
}
if (Array.isArray(paras.shineColor) && paras.shineColor.length === 3) {
uniforms.u_specularColor.value = new THREE.Vector3(...paras.shineColor);
}
if (light && light.dirty) {
if (Array.isArray(light.ambient) && light.ambient.length === 3) {
uniforms.u_ambientColor.value = new THREE.Vector3(...light.ambient);
}
if (Array.isArray(light.position) && light.position.length === 3) {
uniforms.u_lightPos.value = new THREE.Vector3(...light.position);
}
if (typeof light.intensity === "number") {
uniforms.u_lightIntensity.value = light.intensity;
}
if (Array.isArray(light.diffuse) && light.diffuse.length >= 3) {
uniforms.u_color.value = new THREE.Vector4(...light.diffuse, 1);
console.log('update u_color', uniforms.u_color);
}
}
}