scenepic/tssrc/Shaders.ts

339 строки
11 KiB
TypeScript

interface ShaderDef {
type: string;
script: string;
}
const SHADERS = {
"defaultVertex": {
type: "x-shader/x-vertex",
script:
`
// Vertex inputs
attribute vec3 vertexPositionIn;
attribute vec3 vertexNormalIn;
attribute vec3 vertexColorIn;
attribute vec2 vertexTextureIn;
// Instance inputs
attribute vec3 instancePositionIn;
attribute vec4 instanceRotationIn; // Quaternion
attribute vec3 instanceColorIn;
// Where to get color from (0 = per-vertex, 1 = shared-color, 2 = instance-color)
uniform int colorSource;
// Shared Color
uniform vec3 sharedColor;
// Transformation matrices
uniform int useInstanceRotation;
uniform mat4 projMatrix; // Incorporates: model to world to view to screen
uniform mat4 m2vMatrix; // Incorporates: model to world to view
uniform mat3 normMatrix;
// Alpha
uniform float alpha;
// Outputs
varying vec4 vertexColor; // Color with alpha
varying vec2 vertexTextureCoord; // Texture
varying vec3 positionView;
varying vec3 normalView;
void main()
{
// Rotation by quaternion
vec3 vertexPosition = vertexPositionIn;
vec3 vertexNormal = vertexNormalIn;
if (useInstanceRotation == 1)
{
// Apply quaternion rotation to position and normal
// https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
vertexPosition += 2.0 * cross(instanceRotationIn.xyz, cross(instanceRotationIn.xyz, vertexPosition) + instanceRotationIn.w * vertexPosition);
vertexNormal += 2.0 * cross(instanceRotationIn.xyz, cross(instanceRotationIn.xyz, vertexNormal) + instanceRotationIn.w * vertexNormal);
}
vertexPosition += instancePositionIn;
// Transform position and normal
gl_Position = projMatrix * vec4(vertexPosition, 1.0);
positionView = (m2vMatrix * vec4(vertexPosition, 1.0)).rgb; // Unprojected version of above, for fragment shading
normalView = normalize(normMatrix * vertexNormal).rgb;
// Color etc. information
if (colorSource == 2) // Per-instance color
{
vertexColor.rgb = instanceColorIn;
}
else if (colorSource == 1) // Shared color
{
vertexColor.rgb = sharedColor;
}
else // Per vertex color
{
vertexColor.rgb = vertexColorIn;
}
// Set alpha
vertexColor.a = alpha;
// Copy texture coordinates
vertexTextureCoord = vertexTextureIn;
}
`},
"defaultFragment":
{
type: "x-shader/x-fragment",
script:
`
precision mediump float;
// Lighting
uniform float lightingMultiplier;
uniform vec3 ambientLightColor;
uniform vec3 directionalLightDir;
uniform vec3 directionalLightColor;
varying vec4 vertexColor;
varying vec2 vertexTextureCoord;
varying vec3 positionView;
varying vec3 normalView;
uniform int shadingType;
uniform sampler2D sampler; // Represents the texture
// Constants
float FresBias = 0.0;
float FresStren = 1.0;
float FresPow = 2.0;
float GaussConstant = 100.0;
float PI = 3.1415926;
float saturate(float f)
{
return clamp(f, 0.0, 1.0);
}
float f_lambert(vec3 normal, vec3 light)
{
return max(0.0, dot(normal, light));
}
float f_cook_torrance(vec3 normal, vec3 viewer, vec3 light, float roughness)
{
// Compute intermediary values
vec3 half_vector = normalize(light + viewer);
float NdotL = saturate(dot(normal, light));
float NdotH = saturate(dot(normal, half_vector));
float NdotV = saturate(dot(normal, viewer));
float VdotH = saturate(dot(viewer, half_vector));
// Approximate the Fresnel value
float F = FresBias + FresStren * pow((1.0 - NdotV), FresPow);
F = saturate(F);
// Microfacet distribution
float alpha = acos(NdotH);
float D = GaussConstant * exp(-(alpha * alpha) / (roughness * roughness));
// Geometric attenuation factor
float G = min(1.0, min((2.0 * NdotH * NdotV / VdotH), (2.0 * NdotH * NdotL / VdotH)));
return saturate(max(0.001, ((F * D * G) / (PI * NdotV * NdotL))));
}
void main()
{
if (shadingType == -1) // Vertex shading only, not pixel shading
{
gl_FragColor = vertexColor;
gl_FragColor.rgb *= lightingMultiplier;
}
else
{
vec3 L = normalize(directionalLightDir);
vec3 V = normalize(-positionView);
vec3 N = normalize(normalView);
// Diffuse color and alpha
vec3 diffCol;
float alpha;
if (shadingType == 0) // Vertex color
{
diffCol = vertexColor.rgb;
alpha = vertexColor.a;
}
else if (shadingType == 1 || shadingType == 2) // Texture
{
vec4 texCol = texture2D(sampler, vertexTextureCoord);
diffCol = texCol.rgb;
alpha = texCol.a * vertexColor.a; // Preserve texture alpha
}
else // ERROR: Unknown shader
{
diffCol = vec3(1.0, 0.0, 0.0);
alpha = 1.0;
}
// Specular color
vec3 specCol = vec3(1.0, 1.0, 1.0) * 0.75;
// Lighting
gl_FragColor.rgb = ambientLightColor * diffCol;
if (shadingType != 2) // Don't apply this to labels
{
gl_FragColor.rgb += directionalLightColor * diffCol * f_lambert(N, L);
gl_FragColor.rgb += directionalLightColor * specCol * f_cook_torrance(N, V, L, 0.2);
gl_FragColor.rgb *= lightingMultiplier;
}
gl_FragColor.a = alpha;
}
}
`
},
"pickerVertex": {
type: "x-shader/x-vertex",
script:
`#version 300 es
// Vertex inputs
in vec3 vertexPositionIn;
// Instance inputs
in vec3 instancePositionIn;
in vec4 instanceRotationIn; // Quaternion
// Transformation matrices
uniform int useInstanceRotation;
uniform mat4 projMatrix; // Incorporates: model to world to view to screen
uniform mat4 m2vMatrix; // Incorporates: model to world to view
void main()
{
// Rotation by quaternion
vec3 vertexPosition = vertexPositionIn;
if (useInstanceRotation == 1)
{
// Apply quaternion rotation to position and normal
// https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
vertexPosition += 2.0 * cross(instanceRotationIn.xyz, cross(instanceRotationIn.xyz, vertexPosition) + instanceRotationIn.w * vertexPosition);
}
vertexPosition += instancePositionIn;
// Transform position and normal
gl_Position = projMatrix * vec4(vertexPosition, 1.0);
}
`},
"pickerFragment": {
type: "x-shader/x-fragment",
script:
`#version 300 es
precision mediump float;
uniform int u_id;
out vec4 o_fragColor;
void main() {
float r = float(u_id & 0xFF);
float g = float((u_id >> 8) & 0xFF);
float b = float((u_id >> 16) & 0xFF);
o_fragColor = vec4(vec3(r, g, b) / 255.0, 1.0);
}
`
}
};
// Generates a shader from the webgl context and id of an html script element containing the shader code
function makeShader(gl: WebGL2RenderingContext, shaderDef: ShaderDef) {
// Create shader object
var shader: WebGLShader;
if (shaderDef.type == "x-shader/x-fragment")
shader = gl.createShader(gl.FRAGMENT_SHADER);
else if (shaderDef.type == "x-shader/x-vertex")
shader = gl.createShader(gl.VERTEX_SHADER);
else
return null;
// Compile shader
gl.shaderSource(shader, shaderDef.script);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(shader));
throw new Error("WebGL shader compilation error");
}
return shader;
}
export default class ShaderProgram {
vertexShader: WebGLShader;
fragmentShader: WebGLShader;
program: WebGLProgram;
// Cached attribute locations
vertexPositionAttribLoc: number;
vertexNormalAttribLoc: number;
vertexColorAttribLoc: number;
vertexTextureAttribLoc: number;
instancePositionAttribLoc: number;
instanceRotationAttribLoc: number;
instanceColorAttribLoc: number;
// Cached uniform pointers
useInstanceRotation: WebGLUniformLocation;
projMatrixPtr: WebGLUniformLocation;
m2vMatrixPtr: WebGLUniformLocation;
normMatrixPtr: WebGLUniformLocation;
ambientLightColorPtr: WebGLUniformLocation;
directionalLightDirPtr: WebGLUniformLocation;
directionalLightColorPtr: WebGLUniformLocation;
lightingMultiplierPtr: WebGLUniformLocation;
alphaPtr: WebGLUniformLocation;
sharedColorPtr: WebGLUniformLocation;
colorSourcePtr: WebGLUniformLocation;
shadingTypePtr: WebGLUniformLocation;
samplerPtr: WebGLUniformLocation;
idPtr: WebGLUniformLocation;
constructor(gl: WebGL2RenderingContext, vertexShader: string, fragmentShader: string) {
// Create shaders
this.vertexShader = makeShader(gl, SHADERS[vertexShader]);
this.fragmentShader = makeShader(gl, SHADERS[fragmentShader]);
// Create and use program
this.program = gl.createProgram();
gl.attachShader(this.program, this.vertexShader);
gl.attachShader(this.program, this.fragmentShader);
gl.linkProgram(this.program);
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(this.program));
throw new Error("WebGL Program linking error");
}
gl.useProgram(this.program);
// Store attributes
this.vertexPositionAttribLoc = gl.getAttribLocation(this.program, "vertexPositionIn");
this.vertexNormalAttribLoc = gl.getAttribLocation(this.program, "vertexNormalIn");
this.vertexColorAttribLoc = gl.getAttribLocation(this.program, "vertexColorIn");
this.vertexTextureAttribLoc = gl.getAttribLocation(this.program, "vertexTextureIn");
this.instancePositionAttribLoc = gl.getAttribLocation(this.program, "instancePositionIn");
this.instanceRotationAttribLoc = gl.getAttribLocation(this.program, "instanceRotationIn");
this.instanceColorAttribLoc = gl.getAttribLocation(this.program, "instanceColorIn");
// Store uniforms
this.useInstanceRotation = gl.getUniformLocation(this.program, "useInstanceRotation");
this.projMatrixPtr = gl.getUniformLocation(this.program, "projMatrix");
this.m2vMatrixPtr = gl.getUniformLocation(this.program, "m2vMatrix");
this.normMatrixPtr = gl.getUniformLocation(this.program, "normMatrix");
this.ambientLightColorPtr = gl.getUniformLocation(this.program, "ambientLightColor");
this.directionalLightDirPtr = gl.getUniformLocation(this.program, "directionalLightDir");
this.directionalLightColorPtr = gl.getUniformLocation(this.program, "directionalLightColor");
this.lightingMultiplierPtr = gl.getUniformLocation(this.program, "lightingMultiplier");
this.alphaPtr = gl.getUniformLocation(this.program, "alpha");
this.colorSourcePtr = gl.getUniformLocation(this.program, "colorSource");
this.sharedColorPtr = gl.getUniformLocation(this.program, "sharedColor");
this.shadingTypePtr = gl.getUniformLocation(this.program, "shadingType");
this.samplerPtr = gl.getUniformLocation(this.program, "sampler");
this.idPtr = gl.getUniformLocation(this.program, "u_id");
}
}