From 6d2a0e4d04960806cb699e648b589a497d46ab7c Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Fri, 28 Mar 2025 05:45:32 +0300 Subject: [PATCH] Cube rendered --- .../bgtu/computer_graphics1/main.kt | 160 ++++++++++++----- .../bgtu/computer_graphics1/model.kt | 9 +- .../model_builder/Matrix4x4.kt | 162 ++++++++++++++++++ .../model_builder/Point4F.kt | 16 ++ .../bgtu/computer_graphics1/shaders.kt | 57 +++++- src/jsMain/resources/index.html | 2 +- 6 files changed, 348 insertions(+), 58 deletions(-) create mode 100644 src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Matrix4x4.kt create mode 100644 src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point4F.kt diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/main.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/main.kt index a3afb06..17b03c1 100644 --- a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/main.kt +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/main.kt @@ -6,42 +6,16 @@ import org.khronos.webgl.Float32Array import org.khronos.webgl.Int32Array import org.khronos.webgl.WebGLRenderingContext import org.w3c.dom.HTMLCanvasElement +import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.Matrix4x4 +import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.Point2F +import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.Point3F +import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.Point4F fun main() { val canvas = document.getElementById("canvas")!! as HTMLCanvasElement val gl: dynamic = canvas.getContext("webgl2")!! /*as WebGLRenderingContext*/ - val modelVertexesBuffer = gl.createBuffer() - val textureVertexesBuffer = gl.createBuffer() - val textureTypesBuffer = gl.createBuffer() - - gl.bindBuffer(gl.ARRAY_BUFFER, modelVertexesBuffer) - gl.bufferData( - gl.ARRAY_BUFFER, - Float32Array(model.modelVertexes.toTypedArray()), - gl.STATIC_DRAW - ) - gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 3 * 4, 0) - gl.enableVertexAttribArray(0) - - gl.bindBuffer(gl.ARRAY_BUFFER, textureVertexesBuffer) - gl.bufferData( - gl.ARRAY_BUFFER, - Float32Array(model.textureVertexes.toTypedArray()), - gl.STATIC_DRAW - ) - gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 2 * 4, 0) - gl.enableVertexAttribArray(1) - - gl.bindBuffer(gl.ARRAY_BUFFER, textureTypesBuffer) - gl.bufferData( - gl.ARRAY_BUFFER, - Int32Array(model.textureTypes.map { t -> t.ordinal }.toTypedArray()), - gl.STATIC_DRAW - ) - gl.vertexAttribIPointer(2, 2, gl.INT, false, 1 * 4, 0) - gl.enableVertexAttribArray(2) val vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexShaderSource) @@ -61,26 +35,130 @@ fun main() { gl.attachShader(shaderProgram, fragmentShader) gl.linkProgram(shaderProgram) println("program compilation log: ${gl.getProgramInfoLog(shaderProgram)}") - gl.useProgram(shaderProgram) - gl.clearColor(0f, 0f, 0f, 1f); - gl.clear(gl.COLOR_BUFFER_BIT); - gl.drawArrays(gl.TRIANGLES, 0, model.textureTypes.size / 3) + val transformLocation = gl.getUniformLocation(shaderProgram, "transform") + val projectionLocation = gl.getUniformLocation(shaderProgram, "projection") + val screenLocation = gl.getUniformLocation(shaderProgram, "screen_scale") + + + val modelVertexesBuffer = gl.createBuffer() + val textureVertexesBuffer = gl.createBuffer() + val textureTypesBuffer = gl.createBuffer() + + gl.bindBuffer(gl.ARRAY_BUFFER, modelVertexesBuffer) + println(model.modelVertexes.joinToString()) + gl.bufferData( + gl.ARRAY_BUFFER, + Float32Array(model.modelVertexes.toTypedArray()), + gl.STATIC_DRAW + ) + val vpa = gl.getAttribLocation(shaderProgram, "vertex_pos") + gl.vertexAttribPointer(vpa, 3, gl.FLOAT, false, 0, 0) + gl.enableVertexAttribArray(vpa) + + gl.bindBuffer(gl.ARRAY_BUFFER, textureVertexesBuffer) + gl.bufferData( + gl.ARRAY_BUFFER, + Float32Array(model.textureVertexes.toTypedArray()), + gl.STATIC_DRAW + ) + val tpa = gl.getAttribLocation(shaderProgram, "texture_pos") + gl.vertexAttribPointer(tpa, 2, gl.FLOAT, false, 2 * 4, 0) + gl.enableVertexAttribArray(tpa) + + gl.bindBuffer(gl.ARRAY_BUFFER, textureTypesBuffer) + gl.bufferData( + gl.ARRAY_BUFFER, + Int32Array(model.textureTypes.map { t -> t.ordinal }.toTypedArray()), + gl.STATIC_DRAW + ) + val tta = gl.getAttribLocation(shaderProgram, "texture_type") + gl.vertexAttribIPointer(tta, 2, gl.INT, false, 1 * 4, 0) + gl.enableVertexAttribArray(tta) + + gl.enable(gl.DEPTH_TEST) + + var xAngle = 0.0f + var yAngle = 0.0f + var distance = 0.0f + val xAngleStep = 0.1f + val yAngleStep = 0.1f + val distanceStep = 0.1f + + + val redraw = { + val radius = ((if (canvas.width < canvas.height) canvas.width else canvas.height) * 7f / 16f); + gl.viewport(0, 0, canvas.width, canvas.height); + gl.clearColor(0f, 0f, 0f, 1f); + gl.clear(gl.COLOR_BUFFER_BIT or gl.DEPTH_BUFFER_BIT); + val t = Matrix4x4.shift(0f, 0f, distance) * Matrix4x4.rotateY(yAngle) * Matrix4x4.rotateX(xAngle) + val po = Matrix4x4.projectionOrtho(-2f, 2f, -2f, 2f, -2f, 2f); + val pc = Matrix4x4.projectionCentralInfinite(-2f, 2f, -2f, 2f, 1f) * Matrix4x4.shift(0f, 0f, -3f) * Matrix4x4.scale(3f, 3f, -1f); + val s: Point2F + if (canvas.width > canvas.height) { + s = Point2F(canvas.height.toFloat() / canvas.width.toFloat(), 1f) + } else { + s = Point2F(1f, canvas.width.toFloat() / canvas.height.toFloat()) + } + gl.uniformMatrix4fv(transformLocation, false, t.toFloatArray()) + gl.uniformMatrix4fv(projectionLocation, false, pc.toFloatArray()) + gl.uniform2f(screenLocation, s.x, s.y) + gl.drawArrays(gl.TRIANGLES, 0, model.textureTypes.size) + gl.flush() + } + val p = Matrix4x4.projectionCentral(-2f, 2f, -2f, 2f, 2f, 4f); + + println(p * Point4F(-1f, -1f, -2f, 1f)) + println(p * Point4F(-1f, 1f, -3f, 1f)) + println(p * Point4F(1f, 1f, -4f, 1f)) + + redraw() window.onkeydown = { keyEvent -> when (keyEvent.code) { "ArrowDown" -> { - - gl.clearColor(0f, 0f, 0f, 1f); - gl.clear(gl.COLOR_BUFFER_BIT); - gl.drawArrays(gl.TRIANGLES, 0, model.textureTypes.size / 3) + xAngle += xAngleStep; + redraw() } - "ArrowUp" -> {} - "ArrowLeft" -> {} - "ArrowRight" -> {} + "ArrowUp" -> { + xAngle -= xAngleStep; + redraw() + } + + "ArrowLeft" -> { + yAngle -= yAngleStep; + redraw() + } + + "ArrowRight" -> { + yAngle += yAngleStep; + redraw() + } + + "PageUp" -> { + distance += distanceStep; + redraw() + } + + "PageDown" -> { + distance -= distanceStep; + if (distance < 0) distance = 0f + redraw() + } } } + + window.onresize = { _ -> + canvas.width = document.documentElement!!.clientWidth; + canvas.height = document.documentElement!!.clientHeight; + redraw() + } + window.onload = { _ -> + canvas.width = document.documentElement!!.clientWidth; + canvas.height = document.documentElement!!.clientHeight; + redraw() + } } diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model.kt index 7a8f155..17d5fc1 100644 --- a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model.kt +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model.kt @@ -24,14 +24,12 @@ val model = ModelTriangles.fromPolygons( Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1)), Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)) ), - Polygon( TextureType.HSV, Polygon.PolygonVertex(-1f, -1f, 1f, rot_n(60, 2)), Polygon.PolygonVertex(-1f, 1f, 1f, rot_n(0, 2)), Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1)) ), - Polygon( TextureType.HSV, Polygon.PolygonVertex(-1f, -1f, 1f, rot_n(60, 2)), @@ -45,14 +43,12 @@ val model = ModelTriangles.fromPolygons( Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)), Polygon.PolygonVertex(1f, -1f, 1f, rot_n(120, 1)) ), - Polygon( TextureType.HSV, Polygon.PolygonVertex(1f, -1f, -1f, rot_n(180, 2)), Polygon.PolygonVertex(1f, 1f, -1f, rot_n(240, 2)), Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1)) ), - Polygon( TextureType.HSV, Polygon.PolygonVertex(1f, -1f, -1f, rot_n(180, 2)), @@ -66,14 +62,12 @@ val model = ModelTriangles.fromPolygons( Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)), Polygon.PolygonVertex(1f, -1f, 1f, rot_n(120, 2)) ), - Polygon( TextureType.HSV, Polygon.PolygonVertex(-1f, 1f, -1f, rot_n(300, 2)), Polygon.PolygonVertex(1f, 1f, -1f, rot_n(240, 2)), Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1)) ), - Polygon( TextureType.HSV, Polygon.PolygonVertex(-1f, 1f, -1f, rot_n(300, 2)), @@ -87,6 +81,7 @@ val model = ModelTriangles.fromPolygons( Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1)), Polygon.PolygonVertex(-1f, 1f, 1f, rot_n(0, 2)) ), + Polygon( TextureType.WHITE_TRANSPARENCY, Polygon.PolygonVertex(-1f, -1f, 1f, 0f, 0f), @@ -126,5 +121,5 @@ val model = ModelTriangles.fromPolygons( Polygon.PolygonVertex(-1f, -1f, -1f, 1f, 1f), Polygon.PolygonVertex(-1f, -1f, 1f, 0f, 1f), Polygon.PolygonVertex(1f, -1f, -1f, 1f, 0f) - ), + ) ) \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Matrix4x4.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Matrix4x4.kt new file mode 100644 index 0000000..164be98 --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Matrix4x4.kt @@ -0,0 +1,162 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder + +import kotlin.math.cos +import kotlin.math.sin + +class Matrix4x4 private constructor( + private val v00: Float, + private val v01: Float, + private val v02: Float, + private val v03: Float, + private val v10: Float, + private val v11: Float, + private val v12: Float, + private val v13: Float, + private val v20: Float, + private val v21: Float, + private val v22: Float, + private val v23: Float, + private val v30: Float, + private val v31: Float, + private val v32: Float, + private val v33: Float +) { + private inline fun _foldMatrix(other: Matrix4x4, operator: (rowIndex: Int, columnIndex: Int, self: Float, other: Float) -> Float): Matrix4x4 = Matrix4x4( + operator(0, 0, this.v00, other.v00), operator(0, 1, this.v01, other.v01), operator(0, 2, this.v02, other.v02), operator(0, 3, this.v03, other.v03), + operator(1, 0, this.v10, other.v10), operator(1, 1, this.v11, other.v11), operator(1, 2, this.v12, other.v12), operator(1, 3, this.v13, other.v13), + operator(2, 0, this.v20, other.v20), operator(2, 1, this.v21, other.v21), operator(2, 2, this.v22, other.v22), operator(2, 3, this.v23, other.v23), + operator(3, 0, this.v30, other.v30), operator(3, 1, this.v31, other.v31), operator(3, 2, this.v32, other.v32), operator(3, 3, this.v33, other.v33), + ) + + operator fun get(row: Int, column: Int) = when (row * 4 + column) { + 0 -> this.v00 + 1 -> this.v01 + 2 -> this.v02 + 3 -> this.v03 + 4 -> this.v10 + 5 -> this.v11 + 6 -> this.v12 + 7 -> this.v13 + 8 -> this.v20 + 9 -> this.v21 + 10 -> this.v22 + 11 -> this.v23 + 12 -> this.v30 + 13 -> this.v31 + 14 -> this.v32 + 15 -> this.v33 + else -> throw IllegalArgumentException() + } + + operator fun times(other: Matrix4x4): Matrix4x4 = + this._foldMatrix(other) { y, x, _, _ -> + return@_foldMatrix this[y, 0] * other[0, x] + this[y, 1] * other[1, x] + this[y, 2] * other[2, x] + this[y, 3] * other[3, x]; + } + + fun toFloatArray() = floatArrayOf( + this.v00, this.v01, this.v02, this.v03, + this.v10, this.v11, this.v12, this.v13, + this.v20, this.v21, this.v22, this.v23, + this.v30, this.v31, this.v32, this.v33 + ) + + fun transposed() = Matrix4x4( + this.v00, this.v10, this.v20, this.v30, + this.v01, this.v11, this.v21, this.v31, + this.v02, this.v12, this.v22, this.v32, + this.v03, this.v13, this.v23, this.v33, + ) + + operator fun times(p: Point3F) = Point3F( + x = this.v00 * p.x + this.v01 * p.y + this.v02 * p.z + this.v03, + y = this.v10 * p.x + this.v11 * p.y + this.v12 * p.z + this.v13, + z = this.v20 * p.x + this.v21 * p.y + this.v22 * p.z + this.v23, + ) + + operator fun times(p: Point4F) = Point4F( + x = this.v00 * p.x + this.v01 * p.y + this.v02 * p.z + this.v03, + y = this.v10 * p.x + this.v11 * p.y + this.v12 * p.z + this.v13, + z = this.v20 * p.x + this.v21 * p.y + this.v22 * p.z + this.v23, + w = this.v30 * p.x + this.v31 * p.y + this.v32 * p.z + this.v33, + ) + + companion object { + val I = Matrix4x4( + 1f, 0f, 0f, 0f, + 0f, 1f, 0f, 0f, + 0f, 0f, 1f, 0f, + 0f, 0f, 0f, 1f + ) + + fun scale(x: Float, y: Float, z: Float) = Matrix4x4( + x, 0f, 0f, 0f, + 0f, y, 0f, 0f, + 0f, 0f, z, 0f, + 0f, 0f, 0f, 1f + ) + + fun scale(s: Float) = this.scale(s, s, s) + + fun rotateX(rad: Float): Matrix4x4 { + val c = cos(rad) + val s = sin(rad) + return Matrix4x4( + 1f, 0f, 0f, 0f, + 0f, c, -s, 0f, + 0f, s, c, 0f, + 0f, 0f, 0f, 1f + ) + } + + fun rotateY(rad: Float): Matrix4x4 { + val c = cos(rad) + val s = sin(rad) + return Matrix4x4( + c, 0f, s, 0f, + 0f, 1f, 0f, 0f, + -s, 0f, c, 0f, + 0f, 0f, 0f, 1f + ) + } + + fun rotateZ(rad: Float): Matrix4x4 { + val c = cos(rad) + val s = sin(rad) + return Matrix4x4( + c, -s, 0f, 0f, + s, c, 0f, 0f, + 0f, 0f, 1f, 0f, + 0f, 0f, 0f, 1f + ) + } + + fun shift(x: Float, y: Float, z: Float) = Matrix4x4( + 1f, 0f, 0f, x, + 0f, 1f, 0f, y, + 0f, 0f, 1f, z, + 0f, 0f, 0f, 1f + ) + + fun projectionOrtho(l: Float, r: Float, b: Float, t: Float, n: Float, f: Float) = Matrix4x4( + 2f / (r - l), 0f, 0f, -(r + l) / (r - l), + 0f, 2f / (t - b), 0f, -(t + b) / (t - b), + 0f, 0f, 2f / (f - n), -(f + n) / (f - n), + 0f, 0f, 0f, 1f + ) + + fun projectionCentral(l: Float, r: Float, b: Float, t: Float, n: Float, f: Float) = Matrix4x4( + 2f * n / (r - l), 0f, (r + l) / (r - l), 0f, + 0f, 2f * n / (t - b), (t + b) / (t - b), 0f, + 0f, 0f, -(f + n) / (f - n), -2f * n * f / (f - n), + 0f, 0f, -1f, 0f + ) + + fun projectionCentralInfinite(l: Float, r: Float, b: Float, t: Float, n: Float) = Matrix4x4( + 2f * n / (r - l), 0f, (r + l) / (r - l), 0f, + 0f, 2f * n / (t - b), (t + b) / (t - b), 0f, + 0f, 0f, -1f, -2f * n, + 0f, 0f, -1f, 0f + ) + } + +} \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point4F.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point4F.kt new file mode 100644 index 0000000..ede9ca6 --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point4F.kt @@ -0,0 +1,16 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder + +class Point4F( + val x: Float, + val y: Float, + val z: Float, + val w: Float, +) { + operator fun component1(): Float = this.x + operator fun component2(): Float = this.y + operator fun component3(): Float = this.z + operator fun component4(): Float = this.w + + override fun toString(): String = + "" +} \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/shaders.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/shaders.kt index e33f69a..c8337be 100644 --- a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/shaders.kt +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/shaders.kt @@ -6,14 +6,19 @@ internal val vertexShaderSource = """ #version 300 es uniform mat4 transform; - layout(location = 0) in vec3 vertex_pos; - layout(location = 1) in vec2 texture_pos; - layout(location = 2) in int texture_type; + uniform mat4 projection; + uniform vec2 screen_scale; + in vec3 vertex_pos; + in vec2 texture_pos; + in int texture_type; out vec2 texture_pos_v2f; flat out int texture_type_v2f; void main() { - gl_Position = vec4(vertex_pos.xyz, 1) * transform; + gl_Position = vec4(vertex_pos, 1.0); + gl_Position *= transform; + gl_Position *= projection; + gl_Position.xy *= screen_scale; texture_pos_v2f = texture_pos; texture_type_v2f = texture_type; } @@ -21,27 +26,61 @@ internal val vertexShaderSource = """ internal val fragmentShaderSource = """ #version 300 es + #define PI 3.1415926535897932384626433832795 precision mediump float; in vec2 texture_pos_v2f; flat in int texture_type_v2f; - out vec4 color; + out vec4 FragColor; + + vec3 hsv2rgb(float, float, float); + vec3 hsv_texture(float, float); void main() { switch (texture_type_v2f) { case ${TextureType.WHITE_TRANSPARENCY.ordinal}: - color = vec4(255, 255, 255, 127); + FragColor = vec4(1.0, 1.0, 1.0, 0.5); break; case ${TextureType.CHESS.ordinal}: if (int( round(floor(texture_pos_v2f.x / 0.1)) + round(floor(texture_pos_v2f.y / 0.1)) ) % 2 == 0) { - color = vec4(0, 0, 0, 255); + FragColor = vec4(0.0, 0.0, 0.0, 1.0); } else { - color = vec4(255, 255, 255, 255); + FragColor = vec4(1.0, 1.0, 1.0, 1.0); } break; case ${TextureType.HSV.ordinal}: + FragColor = vec4(hsv_texture(texture_pos_v2f.x, texture_pos_v2f.y), 1.0); + break; default: - color = vec4(0, 0, 0, 0); + FragColor = vec4(0.0, 0.0, 0.0, 0.0); + } + } + + vec3 hsv_texture(float x, float y) { + float angle = atan(y, x); + return hsv2rgb(mod(mod(angle / PI * 180.0, 360.0) + 360.0, 360.0), 1.0, 1.0); + } + + vec3 hsv2rgb(float hue, float saturation, float value) { + int hi = int(floor(hue / 60.0)) % 6; + float f = hue / 60.0 - floor(hue / 60.0); + float v = value; + float p = value * (1.0 - saturation); + float q = value * (1.0 - f * saturation); + float t = value * (1.0 - (1.0 - f) * saturation); + switch (hi) { + case 0: + return vec3(v, t, p); + case 1: + return vec3(q, v, p); + case 2: + return vec3(p, v, t); + case 3: + return vec3(p, q, v); + case 4: + return vec3(t, p, v); + default: + return vec3(v, p, q); } } """.trimIndent() \ No newline at end of file diff --git a/src/jsMain/resources/index.html b/src/jsMain/resources/index.html index 98bd8cb..8c15623 100644 --- a/src/jsMain/resources/index.html +++ b/src/jsMain/resources/index.html @@ -5,7 +5,7 @@ Title - +