Compilable WebGL program

This commit is contained in:
Andrew Golovashevich 2025-03-27 23:30:16 +03:00
commit 92603d8faf
13 changed files with 427 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
/.idea/
/gradle/
/.gradle/
/build/
*.class
*.jar
/out/
/gradlew*
/.kotlin/
*.js

28
build.gradle.kts Normal file
View File

@ -0,0 +1,28 @@
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
plugins {
kotlin("multiplatform") version "2.1.10"
}
repositories {
mavenCentral()
}
kotlin {
js {
binaries.executable()
browser {
commonWebpackConfig {
outputFileName = "computer-graphics-1.js"
}
}
}
sourceSets {
jsMain {
dependencies {
implementation(kotlin("stdlib"))
}
}
}
}

1
gradle.properties Normal file
View File

@ -0,0 +1 @@
kotlin.code.style=official

1
settings.gradle.kts Normal file
View File

@ -0,0 +1 @@
rootProject.name = "cg-js"

View File

@ -0,0 +1,86 @@
package ru.landgrafhomyak.bgtu.computer_graphics1
import kotlinx.browser.document
import kotlinx.browser.window
import org.khronos.webgl.Float32Array
import org.khronos.webgl.Int32Array
import org.khronos.webgl.WebGLRenderingContext
import org.w3c.dom.HTMLCanvasElement
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)
gl.compileShader(vertexShader)
println("vertex shader compilation status: ${gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)}")
println("vertex shader compilation log: ${gl.getShaderInfoLog(vertexShader)}")
val fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragmentShader, fragmentShaderSource)
gl.compileShader(fragmentShader)
println("fragment shader compilation status: ${gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)}")
println("fragment shader compilation log: ${gl.getShaderInfoLog(fragmentShader)}")
val shaderProgram = gl.createProgram()
gl.attachShader(shaderProgram, vertexShader)
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)
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)
}
"ArrowUp" -> {}
"ArrowLeft" -> {}
"ArrowRight" -> {}
}
}
}

View File

@ -0,0 +1,130 @@
package ru.landgrafhomyak.bgtu.computer_graphics1
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.ModelTriangles
import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.Point2F
import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.Polygon
import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.TextureType
private fun rot_n(degrees: Int, length: Int): Point2F {
val radians = degrees.toLong().toDouble() * PI / 180;
return Point2F(
x = cos(radians).times(length).toFloat(),
y = sin(radians).times(length).toFloat()
)
}
val model = ModelTriangles.fromPolygons(
Polygon(
TextureType.HSV,
Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1)),
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)),
Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)),
Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1))
),
Polygon(
TextureType.HSV,
Polygon.PolygonVertex(-1f, -1f, 1f, rot_n(60, 2)),
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)),
Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)),
Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1))
),
Polygon(
TextureType.HSV,
Polygon.PolygonVertex(1f, -1f, -1f, rot_n(180, 2)),
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)),
Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1)),
Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1))
),
Polygon(
TextureType.HSV,
Polygon.PolygonVertex(-1f, 1f, -1f, rot_n(300, 2)),
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),
Polygon.PolygonVertex(-1f, -1f, -1f, 0f, 1f),
Polygon.PolygonVertex(-1f, 1f, 1f, 1f, 0f)
),
Polygon(
TextureType.WHITE_TRANSPARENCY,
Polygon.PolygonVertex(-1f, 1f, -1f, 1f, 1f),
Polygon.PolygonVertex(-1f, -1f, -1f, 0f, 1f),
Polygon.PolygonVertex(-1f, 1f, 1f, 1f, 0f)
),
Polygon(
TextureType.WHITE_TRANSPARENCY,
Polygon.PolygonVertex(-1f, -1f, -1f, 0f, 0f),
Polygon.PolygonVertex(-1f, 1f, -1f, 0f, 1f),
Polygon.PolygonVertex(1f, 1f, -1f, 1f, 1f)
),
Polygon(
TextureType.WHITE_TRANSPARENCY,
Polygon.PolygonVertex(-1f, -1f, -1f, 0f, 0f),
Polygon.PolygonVertex(1f, -1f, -1f, 1f, 0f),
Polygon.PolygonVertex(1f, 1f, -1f, 1f, 1f)
),
Polygon(
TextureType.CHESS,
Polygon.PolygonVertex(1f, -1f, 1f, 0f, 0f),
Polygon.PolygonVertex(-1f, -1f, 1f, 0f, 1f),
Polygon.PolygonVertex(1f, -1f, -1f, 1f, 0f)
),
Polygon(
TextureType.CHESS,
Polygon.PolygonVertex(-1f, -1f, -1f, 1f, 1f),
Polygon.PolygonVertex(-1f, -1f, 1f, 0f, 1f),
Polygon.PolygonVertex(1f, -1f, -1f, 1f, 0f)
),
)

View File

@ -0,0 +1,50 @@
package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder
class ModelTriangles(
val modelVertexes: FloatArray,
val textureVertexes: FloatArray,
val textureTypes: Array<TextureType>
) {
companion object {
private fun <T> MutableCollection<T>.addAll(vararg elems: T) {
this.addAll(elems)
}
fun fromPolygons(vararg polygons: Polygon): ModelTriangles {
val modelVertexes = ArrayList<Float>()
val textureVertexes = ArrayList<Float>()
val textureTypes = ArrayList<TextureType>()
polygons@ for (polygon in polygons) {
val it = polygon.vertexes.iterator()
if (!it.hasNext()) continue@polygons
val root = it.next()
if (!it.hasNext()) continue@polygons
var prev = it.next()
for (curr in it) {
modelVertexes.addAll(
root.modelVertex.x, root.modelVertex.y, root.modelVertex.z,
prev.modelVertex.x, prev.modelVertex.y, prev.modelVertex.z,
curr.modelVertex.x, curr.modelVertex.y, curr.modelVertex.z,
)
textureVertexes.addAll(
root.textureVertex.x, root.textureVertex.y,
prev.textureVertex.x, prev.textureVertex.y,
curr.textureVertex.x, curr.textureVertex.y,
)
textureTypes.addAll(
polygon.textureType, polygon.textureType, polygon.textureType
)
prev = curr
}
}
return ModelTriangles(
modelVertexes = modelVertexes.toTypedArray().toFloatArray(),
textureVertexes = textureVertexes.toTypedArray().toFloatArray(),
textureTypes = textureTypes.toTypedArray()
)
}
}
}

View File

@ -0,0 +1,12 @@
package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder
class Point2F(
val x: Float,
val y: Float,
) {
operator fun component1(): Float = this.x
operator fun component2(): Float = this.y
override fun toString(): String =
"<point 2f x=${this.x} y=${this.y}>"
}

View File

@ -0,0 +1,14 @@
package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder
class Point3F(
val x: Float,
val y: Float,
val z: Float,
) {
operator fun component1(): Float = this.x
operator fun component2(): Float = this.y
operator fun component3(): Float = this.z
override fun toString(): String =
"<point 3f x=${this.x} y=${this.y} z=${this.z}>"
}

View File

@ -0,0 +1,29 @@
package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder
class Polygon(
val textureType: TextureType,
vararg val vertexes: PolygonVertex
) {
init {
require(this.vertexes.size >= 3) { "Polygon should has at least 3 vertexes" }
}
class PolygonVertex(
val modelVertex: Point3F,
val textureVertex: Point2F
) {
operator fun component1() = this.modelVertex
operator fun component2() = this.textureVertex
constructor(mx: Float, my: Float, mz: Float, tx: Float, ty: Float) :
this(Point3F(mx, my, mz), Point2F(tx, ty))
constructor(m: Point3F, tx: Float, ty: Float) :
this(m, Point2F(tx, ty))
constructor(mx: Float, my: Float, mz: Float, t: Point2F) :
this(Point3F(mx, my, mz), t)
}
}

View File

@ -0,0 +1,7 @@
package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder
enum class TextureType {
CHESS,
HSV,
WHITE_TRANSPARENCY,
}

View File

@ -0,0 +1,47 @@
package ru.landgrafhomyak.bgtu.computer_graphics1
import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.TextureType
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;
out vec2 texture_pos_v2f;
flat out int texture_type_v2f;
void main() {
gl_Position = vec4(vertex_pos.xyz, 1) * transform;
texture_pos_v2f = texture_pos;
texture_type_v2f = texture_type;
}
""".trimIndent()
internal val fragmentShaderSource = """
#version 300 es
precision mediump float;
in vec2 texture_pos_v2f;
flat in int texture_type_v2f;
out vec4 color;
void main() {
switch (texture_type_v2f) {
case ${TextureType.WHITE_TRANSPARENCY.ordinal}:
color = vec4(255, 255, 255, 127);
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);
} else {
color = vec4(255, 255, 255, 255);
}
break;
case ${TextureType.HSV.ordinal}:
default:
color = vec4(0, 0, 0, 0);
}
}
""".trimIndent()

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<canvas style="left: 0; top: 0; right: 0; bottom:0; position: fixed" id="canvas"></canvas>
<script src="computer-graphics-1.js"></script>
</body>
</html>