@tolokoban/tgd
Version:
ToloGameDev library for WebGL2
171 lines • 16.6 kB
JavaScript
import { tgdColorMakeHueWheel } from "./../../color/index.js";
import { TgdDataset } from "./../../dataset/index.js";
import { TgdProgram } from "./../../program/index.js";
import { TgdShaderFragment, TgdShaderVertex } from "./../../shader/index.js";
import { TgdTexture2D } from "./../../texture/index.js";
import { tgdCanvasCreateGradientHorizontal } from "./../../utils/index.js";
import { TgdVertexArray } from "./../../vao/index.js";
import { TgdPainter } from "../painter.js";
export class TgdPainterPointsCloud extends TgdPainter {
/**
* Draw spheres with simple diffuse/specular material.
*/
static fragCodeSphere(options = {}) {
const { specularExponent, specularIntensity, shadowThickness, shadowIntensity, light } = options;
const SpE = typeof specularExponent === "number" ? specularExponent.toFixed(6) : "uniSpecularExponent";
const SpI = typeof specularIntensity === "number" ? specularIntensity.toFixed(6) : "uniSpecularIntensity";
const ShT = typeof shadowThickness === "number" ? shadowThickness.toFixed(6) : "uniShadowThickness";
const ShI = typeof shadowIntensity === "number" ? shadowIntensity.toFixed(6) : "uniShadowIntensity";
const L = typeof light === "number" ? light.toFixed(6) : "uniLight";
return [
"vec2 coords = 2.0 * (gl_PointCoord - vec2(.5));",
"float len = 1.0 - dot(coords, coords);",
"if (len < 0.0) discard;",
"gl_FragDepth = gl_FragCoord.z - len * 1e-5;",
`float light = smoothstep(0.0, ${ShT}, len) * ${ShI} + (1.0 - ${ShI});`,
specularIntensity ? `float spec = pow(len, ${SpE}) * ${SpI};` : "// No specular.",
`return color * vec4(vec3(light * ${L}), 1.0) + vec4(vec3(spec), 0.0);`,
];
}
constructor(context, options) {
super();
this.context = context;
this.radiusMultiplier = 1;
this.minSizeInPixels = 0;
this.specularExponent = 10;
this.specularIntensity = 0.33;
this.shadowIntensity = 0.5;
this.shadowThickness = 1;
this.light = 1;
this.name = options.name ?? this.name;
this.radiusMultiplier = options.radiusMultiplier ?? 1;
this.minSizeInPixels = options.minSizeInPixels ?? 0;
this.specularExponent = options.specularExponent ?? 10;
this.specularIntensity = options.specularIntensity ?? 0.33;
this.shadowIntensity = options.shadowIntensity ?? 0.5;
this.shadowThickness = options.shadowThickness ?? 1;
this.light = options.light ?? 1;
this.dataPoint = options.dataPoint;
if ((this.dataPoint.length & 3) !== 0) {
throw new Error(`dataPoint must have a length that is an integral multiple of 4: [x, y, z, radius, ...]!\ndataPoint.length === ${this.dataPoint.length}`);
}
this.dataUV = options.dataUV ?? new Float32Array(this.dataPoint.length >> 1);
if (this.dataPoint.length !== this.dataUV.length * 2) {
const message = `dataUV must be half of the size of dataPoint: [u, v, ...]!\ndataPoint.length === ${this.dataPoint.length}, \ndataUV.length === ${this.dataUV.length}`;
context.console.error("[TgdPainterPointsCloud]", message);
context.console.error("[TgdPainterPointsCloud] options =", options);
context.console.error("[TgdPainterPointsCloud] this.dataPoint =", this.dataPoint);
context.console.error("[TgdPainterPointsCloud] this.dataUV =", this.dataUV);
throw new Error(message);
}
if (options.texture) {
this.texture = options.texture;
this.textureMustBeDeleted = false;
}
else {
this.texture = new TgdTexture2D(context).loadBitmap(tgdCanvasCreateGradientHorizontal(128, tgdColorMakeHueWheel({ luminance: 0.2 })));
this.textureMustBeDeleted = true;
}
this.count = this.dataUV.length >> 1;
this.dataset = this.createDataset();
this.program = this.createProgram(options.fragCode);
this.vao = new TgdVertexArray(context.gl, this.program, [this.dataset]);
}
delete() {
if (this.textureMustBeDeleted)
this.texture.delete();
this.program.delete();
this.vao.delete();
}
paint() {
const { context, program, vao, texture, count, radiusMultiplier, minSizeInPixels } = this;
const { gl, camera } = context;
program.use();
texture.activate(0, program, "uniTexture");
program.uniform1f("uniRadiusMultiplier", radiusMultiplier);
program.uniform1f("uniMinSizeInPixels", minSizeInPixels);
program.uniform1f("uniScreenHeightInPixels", context.height);
program.uniform1f("uniSpecularExponent", this.specularExponent);
program.uniform1f("uniSpecularIntensity", this.specularIntensity);
program.uniform1f("uniShadowIntensity", this.shadowIntensity);
program.uniform1f("uniShadowThickness", this.shadowThickness);
program.uniform1f("uniLight", this.light);
program.uniformMatrix4fv("uniModelViewMatrix", camera.matrixModelView);
program.uniformMatrix4fv("uniProjectionMatrix", camera.matrixProjection);
vao.bind();
gl.drawArrays(gl.POINTS, 0, count);
vao.unbind();
}
createDataset() {
const dataset = new TgdDataset({
attPoint: "vec4",
attUV: "vec2",
});
dataset.set("attPoint", this.dataPoint);
dataset.set("attUV", this.dataUV);
return dataset;
}
createProgram(render) {
const vert = new TgdShaderVertex({
uniforms: {
uniMinSizeInPixels: "float",
uniRadiusMultiplier: "float",
uniScreenHeightInPixels: "float",
uniModelViewMatrix: "mat4",
uniProjectionMatrix: "mat4",
},
attributes: {
attPoint: "vec4",
attUV: "vec2",
},
varying: {
varUV: "vec2",
},
mainCode: [
"varUV = attUV;",
"float radius = attPoint.w;",
"vec4 point = uniModelViewMatrix * vec4(attPoint.xyz, 1.0);",
"vec4 shift = point + vec4(0, uniRadiusMultiplier * radius, 0, 0);",
"gl_Position = uniProjectionMatrix * point;",
"vec4 screenShift = uniProjectionMatrix * shift;",
"gl_PointSize = max(",
" uniMinSizeInPixels,",
" abs(screenShift.y - gl_Position.y) * uniScreenHeightInPixels / gl_Position.w",
");",
],
}).code;
const frag = new TgdShaderFragment({
uniforms: {
uniTexture: "sampler2D",
uniSpecularExponent: "float",
uniSpecularIntensity: "float",
uniShadowIntensity: "float",
uniShadowThickness: "float",
uniLight: "float",
},
varying: {
varUV: "vec2",
},
outputs: { FragColor: "vec4" },
functions: {
render: [
"vec4 render(vec4 color) {",
render ?? [
"vec2 coords = 2.0 * (gl_PointCoord - vec2(.5));",
"float len = 1.0 - dot(coords, coords);",
"if (len < 0.0) discard;",
"gl_FragDepth = gl_FragCoord.z - len * 1e-5;",
`float light = smoothstep(0.0, uniShadowThickness, len) * uniShadowIntensity + (1.0 - uniShadowIntensity);`,
`float spec = pow(len, uniSpecularExponent) * uniSpecularIntensity;`,
"return color * vec4(vec3(light * uniLight), 1.0) + vec4(vec3(spec), 0.0);",
],
"}",
],
},
mainCode: ["vec4 color = texture(uniTexture, varUV);", "FragColor = render(color);"],
}).code;
const program = new TgdProgram(this.context.gl, { vert, frag });
return program;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9pbnRzLWNsb3VkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3BhaW50ZXIvcG9pbnRzLWNsb3VkL3BvaW50cy1jbG91ZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFFakQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGNBQWMsQ0FBQTtBQUN6QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQ3pDLE9BQU8sRUFBZSxpQkFBaUIsRUFBRSxlQUFlLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDN0UsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGNBQWMsQ0FBQTtBQUMzQyxPQUFPLEVBQUUsaUNBQWlDLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFDOUQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUN6QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sWUFBWSxDQUFBO0FBcUV2QyxNQUFNLE9BQU8scUJBQXNCLFNBQVEsVUFBVTtJQUNqRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxjQUFjLENBQ2pCLFVBTUssRUFBRTtRQUVQLE1BQU0sRUFBRSxnQkFBZ0IsRUFBRSxpQkFBaUIsRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBRSxHQUFHLE9BQU8sQ0FBQTtRQUNoRyxNQUFNLEdBQUcsR0FBRyxPQUFPLGdCQUFnQixLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxxQkFBcUIsQ0FBQTtRQUN0RyxNQUFNLEdBQUcsR0FBRyxPQUFPLGlCQUFpQixLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxzQkFBc0IsQ0FBQTtRQUN6RyxNQUFNLEdBQUcs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