176 lines
8.9 KiB
JavaScript
176 lines
8.9 KiB
JavaScript
var gl, program, bufferPosition, bufferNormal, bufferColor, vertexCount = 0;
|
|
|
|
function createShader(glContext, type, source) {
|
|
var shader = glContext.createShader(type);
|
|
glContext.shaderSource(shader, source);
|
|
glContext.compileShader(shader);
|
|
if (!glContext.getShaderParameter(shader, glContext.COMPILE_STATUS))
|
|
throw new Error(glContext.getShaderInfoLog(shader));
|
|
return shader;
|
|
}
|
|
|
|
function initGL() {
|
|
gl = ui.glCanvas.getContext('webgl', {
|
|
antialias: true,
|
|
alpha: true
|
|
});
|
|
if (!gl)
|
|
throw new Error('当前浏览器不支持 WebGL 预览,但仍可导出 STL');
|
|
var vertex = createShader(gl, gl.VERTEX_SHADER, `
|
|
attribute vec3 aPosition;
|
|
attribute vec3 aNormal;
|
|
attribute vec3 aColor;
|
|
uniform mat4 uMVP;
|
|
uniform mat4 uModel;
|
|
varying vec3 vNormal;
|
|
varying vec3 vColor;
|
|
varying vec3 vPos;
|
|
void main() {
|
|
vec4 world = uModel * vec4(aPosition, 1.0);
|
|
gl_Position = uMVP * vec4(aPosition, 1.0);
|
|
vNormal = normalize(mat3(uModel) * aNormal);
|
|
vColor = aColor;
|
|
vPos = world.xyz;
|
|
}
|
|
`);
|
|
var fragment = createShader(gl, gl.FRAGMENT_SHADER, `
|
|
precision mediump float;
|
|
varying vec3 vNormal;
|
|
varying vec3 vColor;
|
|
varying vec3 vPos;
|
|
void main() {
|
|
vec3 n = normalize(vNormal);
|
|
vec3 lightA = normalize(vec3(-0.45, -0.35, 0.82));
|
|
vec3 lightB = normalize(vec3(0.75, 0.25, 0.44));
|
|
float diffuse = max(dot(n, lightA), 0.0) * 0.72 + max(dot(n, lightB), 0.0) * 0.18;
|
|
float rim = pow(1.0 - abs(n.z), 2.0) * 0.18;
|
|
vec3 color = vColor * (0.42 + diffuse) + rim * vec3(0.75, 0.92, 0.90);
|
|
gl_FragColor = vec4(color, 1.0);
|
|
}
|
|
`);
|
|
program = gl.createProgram();
|
|
gl.attachShader(program, vertex);
|
|
gl.attachShader(program, fragment);
|
|
gl.linkProgram(program);
|
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
|
throw new Error(gl.getProgramInfoLog(program));
|
|
bufferPosition = gl.createBuffer();
|
|
bufferNormal = gl.createBuffer();
|
|
bufferColor = gl.createBuffer();
|
|
gl.enable(gl.DEPTH_TEST);
|
|
gl.enable(gl.CULL_FACE);
|
|
gl.cullFace(gl.BACK);
|
|
requestAnimationFrame(renderGL);
|
|
}
|
|
|
|
function updateMeshBuffers() {
|
|
if (!gl || !state.reliefPositions || !state.framePositions)
|
|
return;
|
|
var include = ui.includeFrame.checked;
|
|
var frameLength = include ? state.framePositions.length : 0;
|
|
var totalLength = frameLength + state.reliefPositions.length;
|
|
var positions = new Float32Array(totalLength);
|
|
var normals = new Float32Array(totalLength);
|
|
var colors = new Float32Array(totalLength);
|
|
var offset = 0;
|
|
if (include) {
|
|
positions.set(state.framePositions, 0);
|
|
normals.set(state.frameNormals, 0);
|
|
for (let i = 0; i < frameLength; i += 3) {
|
|
colors[i] = .14;
|
|
colors[i + 1] = .29;
|
|
colors[i + 2] = .31;
|
|
}
|
|
offset = frameLength;
|
|
}
|
|
positions.set(state.reliefPositions, offset);
|
|
normals.set(state.reliefNormals, offset);
|
|
var minT = Number(ui.minThickness.value);
|
|
var maxT = Number(ui.maxThickness.value);
|
|
var thicknessRange = Math.max(.01, maxT - minT);
|
|
for (let i = offset; i < totalLength; i += 3) {
|
|
var thickness = clamp(positions[i + 2] - FAN.baseZ, minT, maxT);
|
|
var darkness = (thickness - minT) / thicknessRange;
|
|
colors[i] = .91 + (.31 - .91) * darkness;
|
|
colors[i + 1] = .97 + (.56 - .97) * darkness;
|
|
colors[i + 2] = .91 + (.54 - .91) * darkness;
|
|
}
|
|
vertexCount = totalLength / 3;
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, bufferPosition);
|
|
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, bufferNormal);
|
|
gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, bufferColor);
|
|
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
|
|
}
|
|
|
|
function mat4Multiply(a, b) {
|
|
var out = new Float32Array(16);
|
|
for (let col = 0; col < 4; col++) {
|
|
for (let row = 0; row < 4; row++) {
|
|
out[col * 4 + row] = a[row] * b[col * 4] + a[4 + row] * b[col * 4 + 1] + a[8 + row] * b[col * 4 + 2] + a[12 + row] * b[col * 4 + 3];
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function perspective(fov, aspect, near, far) {
|
|
var f = 1 / Math.tan(fov / 2);
|
|
var nf = 1 / (near - far);
|
|
return new Float32Array([f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, (far + near) * nf, -1, 0, 0, 2 * far * near * nf, 0]);
|
|
}
|
|
|
|
function translation(x, y, z) {
|
|
return new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1]);
|
|
}
|
|
function rotationX(a) {
|
|
var c = Math.cos(a)
|
|
, s = Math.sin(a);
|
|
return new Float32Array([1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1]);
|
|
}
|
|
function rotationZ(a) {
|
|
var c = Math.cos(a)
|
|
, s = Math.sin(a);
|
|
return new Float32Array([c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
}
|
|
|
|
function renderGL() {
|
|
requestAnimationFrame(renderGL);
|
|
if (!gl || !program || !vertexCount)
|
|
return;
|
|
var dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
var width = Math.max(1, Math.floor(ui.glCanvas.clientWidth * dpr));
|
|
var height = Math.max(1, Math.floor(ui.glCanvas.clientHeight * dpr));
|
|
if (ui.glCanvas.width !== width || ui.glCanvas.height !== height) {
|
|
ui.glCanvas.width = width;
|
|
ui.glCanvas.height = height;
|
|
}
|
|
gl.viewport(0, 0, width, height);
|
|
gl.clearColor(0, 0, 0, 0);
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
gl.useProgram(program);
|
|
|
|
var center = translation(90, 0, -4.5);
|
|
var rotation = mat4Multiply(rotationX(state.camera.pitch), rotationZ(state.camera.yaw));
|
|
var model = mat4Multiply(rotation, center);
|
|
var view = translation(0, 0, -state.camera.zoom);
|
|
var proj = perspective(deg(36), width / height, 1, 1600);
|
|
var mvp = mat4Multiply(proj, mat4Multiply(view, model));
|
|
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uMVP'), false, mvp);
|
|
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uModel'), false, model);
|
|
|
|
var positionLoc = gl.getAttribLocation(program, 'aPosition');
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, bufferPosition);
|
|
gl.enableVertexAttribArray(positionLoc);
|
|
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
|
|
var normalLoc = gl.getAttribLocation(program, 'aNormal');
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, bufferNormal);
|
|
gl.enableVertexAttribArray(normalLoc);
|
|
gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 0, 0);
|
|
var colorLoc = gl.getAttribLocation(program, 'aColor');
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, bufferColor);
|
|
gl.enableVertexAttribArray(colorLoc);
|
|
gl.vertexAttribPointer(colorLoc, 3, gl.FLOAT, false, 0, 0);
|
|
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
|
|
}
|