Toto je série blogových příspěvků souvisejících s WebGL. Nový příspěvek bude k dispozici každý den
Připojte se do seznamu adresátů a získejte nové příspěvky přímo do vaší doručené pošty
Zdrojový kód je k dispozici zde
Postaveno s
Ahoj 👋
Vítejte v měsíci WebGL
Včera jsme vykreslili náš terén minecraftu do textury mimo obrazovku, kde je každý objekt zakódován do určité barvy a naučili jsme se, jak číst barvy pixelů z textury zpět do JS. Nyní dekódujeme tuto barvu na index objektu a zvýrazníme vybranou kostku
gl.readPixels
vyplní Uint8Array
s barvami pixelů začínajícími v levém dolním rohu. Potřebujeme převést souřadnice klienta na souřadnice pixelů v poli. Nezapomeňte na poměr pixelů, protože náš framebuffer mimo obrazovku ho bere v úvahu a souřadnice události ne.
📄 src/minecraft.js
requestAnimationFrame(render);
}
- document.body.addEventListener('click', () => {
+ document.body.addEventListener('click', (e) => {
coloredCubesRenderBuffer.bind(gl);
renderTerrain(gl, viewMatrix, projectionMatrix, true);
const pixels = new Uint8Array(canvas.width * canvas.height * 4);
gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
+
+ const x = e.clientX * devicePixelRatio;
+ const y = (canvas.offsetHeight - e.clientY) * devicePixelRatio;
});
(async () => {
Musíme přeskočit y
řádků (y * canvas.width
) vynásobené 4 (4 celá čísla na pixel)
📄 src/minecraft.js
const x = e.clientX * devicePixelRatio;
const y = (canvas.offsetHeight - e.clientY) * devicePixelRatio;
+
+ const rowsToSkip = y * canvas.width * 4;
});
(async () => {
Vodorovná souřadnice je x * 4
(souřadnice vynásobená počtem celých čísel na pixel)
📄 src/minecraft.js
const y = (canvas.offsetHeight - e.clientY) * devicePixelRatio;
const rowsToSkip = y * canvas.width * 4;
+ const col = x * 4;
});
(async () => {
Takže konečný index pixelu je rowsToSkip + col
📄 src/minecraft.js
const rowsToSkip = y * canvas.width * 4;
const col = x * 4;
+
+ const pixelIndex = rowsToSkip + col;
});
(async () => {
Nyní musíme přečíst každou barevnou složku pixelu
📄 src/minecraft.js
const col = x * 4;
const pixelIndex = rowsToSkip + col;
+
+ const r = pixels[pixelIndex];
+ const g = pixels[pixelIndex + 1];
+ const b = pixels[pixelIndex + 2];
+ const a = pixels[pixelIndex + 3];
});
(async () => {
Nyní musíme převést zpět na celé číslo z r g b
📄 src/minecraft.js
requestAnimationFrame(render);
}
+ function rgbToInt(r, g, b) {
+ return b + g * 255 + r * 255 ** 2;
+ }
+
document.body.addEventListener('click', (e) => {
coloredCubesRenderBuffer.bind(gl);
Pojďme vypustit kód otáčení kamery, aby byla scéna statická
📄 src/minecraft.js
function render() {
offscreenRenderBuffer.clear(gl);
- mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -30]);
- mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
- mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, 30]);
-
- mat4.getTranslation(cameraFocusPoint, cameraFocusPointMatrix);
-
mat4.lookAt(viewMatrix, cameraPosition, cameraFocusPoint, [0, 1, 0]);
renderSkybox(gl, viewMatrix, projectionMatrix);
const g = pixels[pixelIndex + 1];
const b = pixels[pixelIndex + 2];
const a = pixels[pixelIndex + 3];
+
+ const index = rgbToInt(r, g, b);
+
+ console.log(index);
});
(async () => {
a aktualizujte počáteční polohu kamery, abyste viděli scénu lépe
📄 src/minecraft.js
gl.viewport(0, 0, canvas.width, canvas.height);
- const cameraPosition = [0, 5, 0];
- const cameraFocusPoint = vec3.fromValues(0, 0, 30);
+ const cameraPosition = [0, 10, 0];
+ const cameraFocusPoint = vec3.fromValues(30, 0, 30);
const cameraFocusPointMatrix = mat4.create();
mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint);
Dále předáme vybraný index barev do vertex shaderu jako proměnlivý
📄 src/shaders/3d-textured.v.glsl
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
+ uniform float selectedObjectIndex;
varying vec2 vTexCoord;
varying vec3 vColor;
A vynásobte barvu objektu, pokud jeho index odpovídá vybranému indexu objektu
📄 src/shaders/3d-textured.f.glsl
varying vec3 vColor;
uniform float renderIndices;
+ varying vec4 vColorMultiplier;
void main() {
- gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1));
+ gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1)) * vColorMultiplier;
if (renderIndices == 1.0) {
gl_FragColor.rgb = vColor;
📄 src/shaders/3d-textured.v.glsl
varying vec2 vTexCoord;
varying vec3 vColor;
+ varying vec4 vColorMultiplier;
vec3 encodeObject(float id) {
int b = int(mod(id, 255.0));
vTexCoord = texCoord;
vColor = encodeObject(index);
+
+ if (selectedObjectIndex == index) {
+ vColorMultiplier = vec4(1.5, 1.5, 1.5, 1.0);
+ } else {
+ vColorMultiplier = vec4(1.0, 1.0, 1.0, 1.0);
+ }
}
a odrážet změny shaderu v js
📄 src/minecraft-terrain.js
State.ext.vertexAttribDivisorANGLE(State.programInfo.attributeLocations.index, 0);
}
- export function render(gl, viewMatrix, projectionMatrix, renderIndices) {
+ export function render(gl, viewMatrix, projectionMatrix, renderIndices, selectedObjectIndex) {
gl.useProgram(State.program);
setupAttributes(gl);
gl.uniformMatrix4fv(State.programInfo.uniformLocations.viewMatrix, false, viewMatrix);
gl.uniformMatrix4fv(State.programInfo.uniformLocations.projectionMatrix, false, projectionMatrix);
+ gl.uniform1f(State.programInfo.uniformLocations.selectedObjectIndex, selectedObjectIndex);
+
if (renderIndices) {
gl.uniform1f(State.programInfo.uniformLocations.renderIndices, 1);
} else {
📄 src/minecraft.js
gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height);
+ let selectedObjectIndex = -1;
+
function render() {
offscreenRenderBuffer.clear(gl);
mat4.lookAt(viewMatrix, cameraPosition, cameraFocusPoint, [0, 1, 0]);
renderSkybox(gl, viewMatrix, projectionMatrix);
- renderTerrain(gl, viewMatrix, projectionMatrix);
+ renderTerrain(gl, viewMatrix, projectionMatrix, false, selectedObjectIndex);
gl.useProgram(program);
const index = rgbToInt(r, g, b);
- console.log(index);
+ selectedObjectIndex = index;
});
(async () => {
A je to! Nyní známe vybraný index objektu, takže můžeme provádět operace JS i vizuální zpětnou vazbu!
Děkujeme za přečtení!
Připojte se do seznamu adresátů a získejte nové příspěvky přímo do vaší doručené pošty
Zdrojový kód je k dispozici zde
Postaveno s