Added support for Textures

This commit is contained in:
Maciej Samborski 2024-12-24 20:37:22 +01:00
parent 129cf05b90
commit 2c81bc0684
25 changed files with 164 additions and 54 deletions

View File

@ -0,0 +1,3 @@
For a description of the contents of this pack, usage rights, and other useful details, go here:
http://blog.spiralgraphics.biz/2011/02/nine-cartoon-backdrops.html

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -1,4 +1,5 @@
import { Vec2 } from "./common.js"; import { Vec2 } from "./common.js";
import { Texture } from "./graphics.js";
function drawTriangle(gfx, positions, color) { function drawTriangle(gfx, positions, color) {
const a_position = gfx.getAttribute("a_position"); const a_position = gfx.getAttribute("a_position");
const a_color = gfx.getAttribute("a_color"); const a_color = gfx.getAttribute("a_color");
@ -35,7 +36,6 @@ function drawTriangleExts(gfx, position, exts, color) {
} }
function drawRectangle(gfx, position, exts, color) { function drawRectangle(gfx, position, exts, color) {
const a_position = gfx.getAttribute("a_position"); const a_position = gfx.getAttribute("a_position");
const a_color = gfx.getAttribute("a_color");
const points = [ const points = [
position.x, position.y, position.x, position.y,
position.x + exts.x, position.y, position.x + exts.x, position.y,
@ -44,6 +44,23 @@ function drawRectangle(gfx, position, exts, color) {
position.x + exts.x, position.y, position.x + exts.x, position.y,
position.x, position.y + exts.y, position.x, position.y + exts.y,
]; ];
if (color instanceof Texture) {
const a_tex_position = gfx.getAttribute("a_tex_position");
const uv = [
0.0, 0.0,
-1.0, 0.0,
0.0, -1.0,
-1.0, -1.0,
-1.0, 0.0,
0.0, -1.0,
];
a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW);
a_tex_position.data(gfx.ctx, uv, gfx.ctx.STATIC_DRAW);
gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, color.tex);
gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6);
}
else {
const a_color = gfx.getAttribute("a_color");
const colors = [ const colors = [
color, color,
color, color,
@ -56,6 +73,7 @@ function drawRectangle(gfx, position, exts, color) {
a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW);
gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6);
} }
}
function drawCircle(gfx, position, radius, color) { function drawCircle(gfx, position, radius, color) {
const points = new Array(); const points = new Array();
const precision = 40; const precision = 40;

View File

@ -1,5 +1,5 @@
import { Vec2, Color } from "./common.js" import { Vec2, Color } from "./common.js"
import { Graphics } from "./graphics.js"; import { Graphics, Texture } from "./graphics.js";
function drawTriangle(gfx: Graphics, positions: [Vec2, Vec2, Vec2], color: Color) { function drawTriangle(gfx: Graphics, positions: [Vec2, Vec2, Vec2], color: Color) {
const a_position = gfx.getAttribute("a_position"); const a_position = gfx.getAttribute("a_position");
@ -43,9 +43,8 @@ function drawTriangleExts(gfx: Graphics, position: Vec2, exts: Vec2, color: Colo
gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3);
} }
function drawRectangle(gfx: Graphics, position: Vec2, exts: Vec2, color: Color) { function drawRectangle(gfx: Graphics, position: Vec2, exts: Vec2, color: Color | Texture) {
const a_position = gfx.getAttribute("a_position"); const a_position = gfx.getAttribute("a_position");
const a_color = gfx.getAttribute("a_color");
const points: Array<number> = [ const points: Array<number> = [
position.x, position.y, position.x, position.y,
@ -57,6 +56,25 @@ function drawRectangle(gfx: Graphics, position: Vec2, exts: Vec2, color: Color)
position.x, position.y + exts.y, position.x, position.y + exts.y,
] ]
if (color instanceof Texture) {
const a_tex_position = gfx.getAttribute("a_tex_position");
const uv: Array<number> = [
0.0, 0.0,
-1.0, 0.0,
0.0, -1.0,
-1.0, -1.0,
-1.0, 0.0,
0.0, -1.0,
];
a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW);
a_tex_position.data(gfx.ctx, uv, gfx.ctx.STATIC_DRAW);
gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, color.tex);
gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6);
} else {
const a_color = gfx.getAttribute("a_color");
const colors: Array<number[]> = [ const colors: Array<number[]> = [
color, color,
color, color,
@ -69,6 +87,7 @@ function drawRectangle(gfx: Graphics, position: Vec2, exts: Vec2, color: Color)
a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW);
a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW);
gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6);
}
} }

View File

@ -107,4 +107,30 @@ class Attribute {
ctx.vertexAttribPointer(this.loc, this.size, this.type, this.normalized, this.stride, this.offset); ctx.vertexAttribPointer(this.loc, this.size, this.type, this.normalized, this.stride, this.offset);
} }
} }
export { fullscreenCanvas, Graphics, Attribute }; class Texture {
tex;
constructor(tex) {
this.tex = tex;
}
static async load(ctx, path) {
let image = await loadTexture(path);
let tex = ctx.createTexture();
ctx.bindTexture(ctx.TEXTURE_2D, tex);
ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_NEAREST);
ctx.generateMipmap(ctx.TEXTURE_2D);
ctx.bindTexture(ctx.TEXTURE_2D, null);
return new Texture(tex);
}
}
async function loadTexture(path) {
return new Promise(resolve => {
const img = new Image();
img.addEventListener("load", () => {
resolve(img);
});
img.src = path;
});
}
export { Texture, fullscreenCanvas, Graphics, Attribute };

View File

@ -1,5 +1,3 @@
import { Vec2 } from "./common.js"
function fullscreenCanvas(gfx: Graphics, id: string) { function fullscreenCanvas(gfx: Graphics, id: string) {
const canvas = document.getElementById(id) as HTMLCanvasElement; const canvas = document.getElementById(id) as HTMLCanvasElement;
canvas.width = window.innerWidth; canvas.width = window.innerWidth;
@ -145,4 +143,36 @@ class Attribute {
} }
} }
export { fullscreenCanvas, Graphics, Attribute } class Texture {
tex: WebGLTexture | null;
constructor(tex: WebGLTexture) {
this.tex = tex;
}
static async load(ctx: WebGL2RenderingContext, path: string): Promise<Texture> {
let image = await loadTexture(path);
let tex = ctx.createTexture();
ctx.bindTexture(ctx.TEXTURE_2D, tex);
ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_NEAREST);
ctx.generateMipmap(ctx.TEXTURE_2D);
ctx.bindTexture(ctx.TEXTURE_2D, null);
return new Texture(tex);
}
}
async function loadTexture(path: string): Promise<HTMLImageElement> {
return new Promise(resolve => {
const img = new Image();
img.addEventListener("load", () => {
resolve(img);
})
img.src = path;
});
}
export { Texture, fullscreenCanvas, Graphics, Attribute }

View File

@ -1,32 +1,35 @@
import { initializeContext, Vec2, Mat4 } from "./common.js"; import { initializeContext, Vec2, Mat4 } from "./common.js";
import { Graphics, fullscreenCanvas } from "./graphics.js"; import { Graphics, fullscreenCanvas, Texture } from "./graphics.js";
import * as drawing from "./draw.js"; import * as drawing from "./draw.js";
import * as wasm from "./wasm.js"; import * as wasm from "./wasm.js";
const vertexShader = `#version 300 es const vertexShader = `#version 300 es
in vec2 a_position; in vec2 a_position;
in vec4 a_color; in vec2 a_tex_position;
out vec4 color; out vec2 v_tex_position;
uniform mat4 u_matrix; uniform mat4 u_matrix;
void main() { void main() {
vec4 transformed = u_matrix * vec4(a_position.xy, 0.0, 1.0); vec4 transformed = u_matrix * vec4(a_position.xy, 0.0, 1.0);
gl_Position = transformed; gl_Position = transformed;
color = a_color; v_tex_position = a_tex_position;
} }
`; `;
const fragmentShader = `#version 300 es const fragmentShader = `#version 300 es
precision highp float; precision highp float;
in vec4 color;
in vec2 v_tex_position;
out vec4 outColor; out vec4 outColor;
uniform sampler2D u_texture;
void main() { void main() {
outColor = color; outColor = texture(u_texture, v_tex_position);
} }
`; `;
function draw(gfx, angle) { function draw(gfx, angle, tex) {
gfx.clear(0, 0, 0, 0); gfx.clear(0, 0, 0, 0);
let left = 0; let left = 0;
let right = gfx.ctx.canvas.width; let right = gfx.ctx.canvas.width;
@ -39,7 +42,7 @@ function draw(gfx, angle) {
m = m.mult(Mat4.rotation_y(angle)); m = m.mult(Mat4.rotation_y(angle));
m = m.mult(Mat4.rotation_z(angle)); m = m.mult(Mat4.rotation_z(angle));
gfx.ctx.uniformMatrix4fv(gfx.getUniform("u_matrix"), false, m.splat()); gfx.ctx.uniformMatrix4fv(gfx.getUniform("u_matrix"), false, m.splat());
drawing.drawRectangle(gfx, new Vec2(gfx.ctx.canvas.width / 2 - 50, gfx.ctx.canvas.height / 2 - 50), new Vec2(100, 100), [1, 0, 0, 1]); drawing.drawRectangle(gfx, new Vec2(gfx.ctx.canvas.width / 2 - 200, gfx.ctx.canvas.height / 2 - 200), new Vec2(400, 400), tex);
} }
(async () => { (async () => {
const canvasId = "game"; const canvasId = "game";
@ -50,16 +53,19 @@ function draw(gfx, angle) {
fullscreenCanvas(gfx, canvasId); fullscreenCanvas(gfx, canvasId);
const a_position = gfx.createAttribute("a_position"); const a_position = gfx.createAttribute("a_position");
a_position.format(2, gfx.ctx.FLOAT, false, 0, 0); a_position.format(2, gfx.ctx.FLOAT, false, 0, 0);
const a_color = gfx.createAttribute("a_color"); //const a_color = gfx.createAttribute("a_color");
a_color.format(4, gfx.ctx.FLOAT, false, 0, 0); //a_color.format(4, gfx.ctx.FLOAT, false, 0, 0);
const a_tex_position = gfx.createAttribute("a_tex_position");
a_tex_position.format(2, gfx.ctx.FLOAT, false, 0, 0);
gfx.createUniform("u_matrix"); gfx.createUniform("u_matrix");
let city = await Texture.load(ctx, "../../assets/genetica/rt/City Night.jpg");
let angle = 0; let angle = 0;
let prevTimestamp = 0; let prevTimestamp = 0;
const frame = (timestamp) => { const frame = (timestamp) => {
const deltaTime = (timestamp - prevTimestamp) / 1000; const deltaTime = (timestamp - prevTimestamp) / 1000;
prevTimestamp = timestamp; prevTimestamp = timestamp;
fullscreenCanvas(gfx, "game"); fullscreenCanvas(gfx, "game");
draw(gfx, angle); draw(gfx, angle, city);
angle += Math.PI * deltaTime * 0.5; angle += Math.PI * deltaTime * 0.5;
window.requestAnimationFrame(frame); window.requestAnimationFrame(frame);
}; };

View File

@ -1,5 +1,5 @@
import { initializeContext, Vec2, Mat4, Vec4 } from "./common.js"; import { initializeContext, Vec2, Mat4, Vec4 } from "./common.js";
import { Graphics, fullscreenCanvas } from "./graphics.js"; import { Graphics, fullscreenCanvas, Texture } from "./graphics.js";
import * as drawing from "./draw.js"; import * as drawing from "./draw.js";
import * as wasm from "./wasm.js"; import * as wasm from "./wasm.js";
@ -7,15 +7,15 @@ const vertexShader =
`#version 300 es `#version 300 es
in vec2 a_position; in vec2 a_position;
in vec4 a_color; in vec2 a_tex_position;
out vec4 color; out vec2 v_tex_position;
uniform mat4 u_matrix; uniform mat4 u_matrix;
void main() { void main() {
vec4 transformed = u_matrix * vec4(a_position.xy, 0.0, 1.0); vec4 transformed = u_matrix * vec4(a_position.xy, 0.0, 1.0);
gl_Position = transformed; gl_Position = transformed;
color = a_color; v_tex_position = a_tex_position;
} }
`; `;
@ -23,17 +23,20 @@ const fragmentShader =
`#version 300 es `#version 300 es
precision highp float; precision highp float;
in vec4 color;
in vec2 v_tex_position;
out vec4 outColor; out vec4 outColor;
uniform sampler2D u_texture;
void main() { void main() {
outColor = color; outColor = texture(u_texture, v_tex_position);
} }
`; `;
function draw(gfx: Graphics, angle: number) { function draw(gfx: Graphics, angle: number, tex: Texture) {
gfx.clear(0, 0, 0, 0); gfx.clear(0, 0, 0, 0);
let left = 0; let left = 0;
@ -56,9 +59,9 @@ function draw(gfx: Graphics, angle: number) {
drawing.drawRectangle( drawing.drawRectangle(
gfx, gfx,
new Vec2(gfx.ctx.canvas.width / 2 - 50, gfx.ctx.canvas.height / 2 - 50), new Vec2(gfx.ctx.canvas.width / 2 - 200, gfx.ctx.canvas.height / 2 - 200),
new Vec2(100, 100), new Vec2(400, 400),
[1, 0, 0, 1]); tex);
} }
(async () => { (async () => {
@ -72,11 +75,16 @@ function draw(gfx: Graphics, angle: number) {
const a_position = gfx.createAttribute("a_position"); const a_position = gfx.createAttribute("a_position");
a_position.format(2, gfx.ctx.FLOAT, false, 0, 0); a_position.format(2, gfx.ctx.FLOAT, false, 0, 0);
const a_color = gfx.createAttribute("a_color"); //const a_color = gfx.createAttribute("a_color");
a_color.format(4, gfx.ctx.FLOAT, false, 0, 0); //a_color.format(4, gfx.ctx.FLOAT, false, 0, 0);
const a_tex_position = gfx.createAttribute("a_tex_position");
a_tex_position.format(2, gfx.ctx.FLOAT, false, 0, 0);
gfx.createUniform("u_matrix"); gfx.createUniform("u_matrix");
let city = await Texture.load(ctx, "../../assets/genetica/rt/City Night.jpg");
let angle = 0; let angle = 0;
let prevTimestamp = 0; let prevTimestamp = 0;
const frame = (timestamp: number) => { const frame = (timestamp: number) => {
@ -84,7 +92,7 @@ function draw(gfx: Graphics, angle: number) {
prevTimestamp = timestamp; prevTimestamp = timestamp;
fullscreenCanvas(gfx, "game"); fullscreenCanvas(gfx, "game");
draw(gfx, angle); draw(gfx, angle, city);
angle += Math.PI * deltaTime * 0.5; angle += Math.PI * deltaTime * 0.5;
window.requestAnimationFrame(frame); window.requestAnimationFrame(frame);