opengl LWJGL纹理仅加载为1D线

xqkwcwgp  于 2022-11-04  发布在  其他
关注(0)|答案(1)|浏览(144)

EDIT:为了测试目的,我已经禁用了投影与视图矩阵,它现在可以工作了。我不知道出了什么问题,但我会调查的。

我对LWJGL比较陌生,目前正在尝试加载一个纹理。我可以看到纹理,但它只是一条垂直线,而不是四边形/正方形。我遵循了thisthis教程。我的项目中有一个不同的结构,并试图移动函数,使其更适合我的结构。我检查了每个顶点的UV坐标是否正确,它们是(从技术上讲,图像是旋转的,但它肯定不是Map为一条线)。我以前可以用颜色渲染一个正方形。我还启用了OpenGL调试日志,我得到的消息是:

[LWJGL] OpenGL debug message
    ID: 0x20071
    Source: API
    Type: OTHER
    Severity: NOTIFICATION
    Message: Buffer detailed info: Buffer object 2 (bound to GL_ELEMENT_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.
[LWJGL] OpenGL debug message
    ID: 0x20071
    Source: API
    Type: OTHER
    Severity: NOTIFICATION
    Message: Buffer detailed info: Buffer object 1 (bound to GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (0), GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (1), GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (2), and GL_ARRAY_BUFFER_ARB, usage hint is GL_DYNAMIC_DRAW) will use VIDEO memory as the source for buffer object operations.

我不知道为什么会发生这种情况。我浏览了教程代码,检查了我的代码是否运行了教程所做的所有OpenGL的东西。
下面是相关类中的代码:(克隆资源库并在IDE中打开它可能会更简单)

核心

package com.github.trqhxrd.untitledgame.engine

import com.github.trqhxrd.untitledgame.engine.gui.window.DebugScene
import com.github.trqhxrd.untitledgame.engine.gui.window.Window
import kotlin.system.exitProcess

object Core {

    lateinit var window: Window
    var isShutdownScheduled = false

    fun run() {
        this.init()
        do {
            this.loop()
        } while (!this.isShutdownScheduled)
        this.close()
    }

    private fun init() {
        Thread.currentThread().name = "main"

        this.window = Window(1920, 1080, "The Untitled Game: ")
        this.window.scene = DebugScene()
    }

    private fun loop() = this.window.update()

    private fun close() {
        this.window.destroy()
        exitProcess(0)
    }

    fun shutdown() {
        this.isShutdownScheduled = true
    }
}

窗口

package com.github.trqhxrd.untitledgame.engine.gui.window

import com.github.trqhxrd.untitledgame.engine.Core
import com.github.trqhxrd.untitledgame.engine.gui.util.Time
import org.apache.logging.log4j.LogManager
import org.lwjgl.glfw.GLFW
import org.lwjgl.glfw.GLFWErrorCallback
import org.lwjgl.opengl.GL
import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.GL30
import org.lwjgl.opengl.GLUtil
import org.lwjgl.system.MemoryUtil
import kotlin.math.roundToInt

class Window(
    width: Int,
    height: Int,
    title: String
) {
    private val logger = LogManager.getLogger()!!
    var glfw: Long = -1
        private set
    var title: String = title
        set(value) {
            if (field != value) {
                logger.debug("Changed window title from '$field' to '$value'.")
                field = value
                GLFW.glfwSetWindowTitle(this.glfw, value)
            }
        }
    var width: Int = width
        set(value) {
            if (field != value) {
                GLFW.glfwSetWindowSize(this.glfw, value, this.height)
                GL30.glViewport(0, 0, this.width, this.height)
            }
            field = value
        }
    var height: Int = height
        set(value) {
            if (field != value) {
                GLFW.glfwSetWindowSize(this.glfw, this.width, value)
                GL30.glViewport(0, 0, this.width, this.height)
            }
            field = value
        }

    var scene: Scene? = null
        set(value) {
            field?.stop()
            logger.debug("Switching scene from '${field?.name}' to '${value?.name}'.")
            field = value
            value?.preInit()
            value?.init(this)
            value?.postInit()
        }

    private var beginTime = 0.0
    private var endTime = 0.0
    private var dTime = 0.0
    private var fpsUtil = 0.0

    init {
        this.title = title + randomTitleSuffix()

        GLFWErrorCallback.createPrint(System.err).set()

        if (!GLFW.glfwInit()) throw IllegalStateException("GLFW could not be initialized!")

        GLFW.glfwDefaultWindowHints()
        GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE)
        GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE)
        GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, GLFW.GLFW_TRUE)
        if (GLFW_DEBUG) GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_DEBUG_CONTEXT, GLFW.GLFW_TRUE)

        this.glfw = GLFW.glfwCreateWindow(
            this.width, this.height, this.title,
            MemoryUtil.NULL, MemoryUtil.NULL
        )
        if (this.glfw == MemoryUtil.NULL) throw IllegalStateException("Failed to create window!")

        GLFW.glfwMakeContextCurrent(this.glfw)
        GLFW.glfwSwapInterval(1)
        GL.createCapabilities()

        if (GLFW_DEBUG) GLUtil.setupDebugMessageCallback()

        GLFW.glfwSetWindowSizeCallback(this.glfw) { _: Long, newWidth: Int, newHeight: Int ->
            this.width = newWidth
            this.height = newHeight
            logger.debug("Window '${this.title}' resized to width=$newWidth and height=$newHeight.")
        }

        GLFW.glfwSetWindowCloseCallback(this.glfw) { Core.shutdown() }

        GLFW.glfwShowWindow(this.glfw)
    }

    companion object {
        fun randomTitleSuffix(): String {
            val url = this::class.java.getResource("/assets/title-suffixes.txt")!!
            var suffix: String
            do suffix = url.readText().lines().random()
            while (suffix.isBlank())
            return suffix
        }

        const val GLFW_DEBUG = true
        const val DEBUG_FPS_UPDATE_DELAY = 1
    }

    fun update() {
        GLFW.glfwPollEvents()
        if (this.dTime >= 0) {
            if (this.scene != null) GL11.glClearColor(
                this.scene!!.background.red,
                this.scene!!.background.green,
                this.scene!!.background.blue,
                this.scene!!.background.alpha
            ) else GL11.glClearColor(1f, 1f, 1f, 1f)
            GL11.glClear(GL11.GL_COLOR_BUFFER_BIT)
        }

        this.scene?.preRender()
        this.scene?.render()
        this.scene?.postRender()

        GLFW.glfwSwapBuffers(this.glfw)

        this.endTime = Time.now()
        this.dTime = this.endTime - this.beginTime
        this.beginTime = this.endTime
        if (this.fpsUtil == .0 || (this.fpsUtil + DEBUG_FPS_UPDATE_DELAY < this.endTime)) {
            this.fpsUtil = this.endTime
            logger.debug("FPS: ${(1f / this.dTime).roundToInt()}")
        }
    }

    fun close() = GLFW.glfwSetWindowShouldClose(this.glfw, true)

    fun destroy() {
        GLFW.glfwDestroyWindow(this.glfw);

        GLFW.glfwTerminate();
        GLFW.glfwSetErrorCallback(null)?.free();
    }
}

场景

package com.github.trqhxrd.untitledgame.engine.gui.window

import com.github.trqhxrd.untitledgame.engine.gui.listener.KeyHandler
import com.github.trqhxrd.untitledgame.engine.gui.listener.MouseHandler
import com.github.trqhxrd.untitledgame.engine.gui.rendering.camera.Camera
import com.github.trqhxrd.untitledgame.engine.gui.rendering.Renderer
import com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.ShaderProgram
import com.github.trqhxrd.untitledgame.engine.gui.util.Color
import com.github.trqhxrd.untitledgame.engine.objects.GameObject
import org.lwjgl.opengl.GL30
import java.io.File

@Suppress("LeakingThis")
abstract class Scene(
    val name: String,
    val shader: ShaderProgram = ShaderProgram(name),
    var background: Color = Color.WHITE
) {

    var window: Window? = null
        private set
    val camera = Camera(0f, 0f)
    val mouseHandler = MouseHandler(this)
    val keyHandler = KeyHandler(this)
    val renderer = Renderer(this)
    private val objs = mutableListOf<GameObject>()

    open fun preInit() {}

    open fun init(window: Window) {
        this.window = window
        this.mouseHandler.enable()
        this.keyHandler.enable()
    }

    open fun postInit() {
        this.shader.link()
        GL30.glEnable(GL30.GL_DEPTH_TEST)
    }

    open fun preRender() = this.shader.use()

    open fun render() {
        if (this.validate()) this.renderer.render()
    }

    open fun postRender() = this.shader.detach()

    open fun stop() {
        this.mouseHandler.disable()
        this.keyHandler.disable()
    }

    fun loadVertex(file: File) = this.shader.loadVertex(file)

    fun loadVertex(file: String) = this.loadVertex(File(file))

    fun loadFragment(file: File) = this.shader.loadFragment(file)

    fun loadFragment(file: String) = this.shader.loadFragment(File(file))

    open fun validate() = this.shader.validate()

    open fun validateQuiet() = this.shader.validateQuiet() == 0

    open fun addObject(obj: GameObject) {
        this.objs.add(obj)
        this.renderer.add(obj)
    }

    fun uploadCameraDataToGPU() = this.camera.uploadToGPU(this.shader)
}

调试场景

package com.github.trqhxrd.untitledgame.engine.gui.window

import com.github.trqhxrd.untitledgame.engine.gui.rendering.texture.Texture
import com.github.trqhxrd.untitledgame.engine.gui.util.Color
import com.github.trqhxrd.untitledgame.engine.gui.util.Time
import com.github.trqhxrd.untitledgame.engine.objects.GameObject
import com.github.trqhxrd.untitledgame.engine.objects.components.SpriteRenderer

class DebugScene : Scene("Debug Scene!", background = Color.WHITE) {

    private var last = 0.0

    companion object {
        lateinit var texture: Texture
    }

    override fun init(window: Window) {
        super.init(window)
        this.loadVertex(this::class.java.getResource("/assets/shaders/vertex.glsl")!!.file)
        this.loadFragment(this::class.java.getResource("/assets/shaders/fragment.glsl")!!.file)

        this.validate()

        texture = Texture("/assets/textures/no_texture.png")
        texture.load()
        texture.upload()

        val obj = GameObject("Object", 100, 100, 256, 256)
        obj.add(SpriteRenderer(Color.WHITE))
        this.addObject(obj)
    }

    override fun preRender() {
        super.preRender()
        val dTime = last / Time.now()
        this.last = Time.now()
        this.shader.setUniform("textureSampler", 0)
        this.camera.x -= dTime.toFloat() / 5f
    }
}

材质

package com.github.trqhxrd.untitledgame.engine.gui.rendering.texture

import de.matthiasmann.twl.utils.PNGDecoder
import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL30
import java.nio.ByteBuffer

class Texture(val path: String) {
    val logger = LogManager.getLogger()!!
    val decoder = PNGDecoder(this::class.java.getResourceAsStream(this.path))
    val width = this.decoder.width
    val height = this.decoder.height
    lateinit var buffer: ByteBuffer
        private set
    var id = -1
        private set
        get() {
            if (field == -1) throw NullPointerException("Texture id of ${this.path} requested before texture was loaded.")
            else return field
        }

    companion object {
        const val BYTES_PER_PIXEL = 4
    }

    fun load() {
        this.logger.debug("Loading texture from ${this.path}.")

        this.buffer = ByteBuffer.allocateDirect(this.decoder.width * this.decoder.height * BYTES_PER_PIXEL)
        this.decoder.decode(this.buffer, this.decoder.width * BYTES_PER_PIXEL, PNGDecoder.Format.RGBA)
        this.buffer.flip()

        this.logger.debug("Loaded texture from ${this.path}.")
    }

    fun upload() {
        this.logger.debug("Uploading texture from ${this.path}")

        this.id = GL30.glGenTextures()
        GL30.glBindTexture(GL30.GL_TEXTURE_2D, this.id)
        GL30.glPixelStorei(GL30.GL_UNPACK_ALIGNMENT, 1)
        GL30.glTexImage2D(
            GL30.GL_TEXTURE_2D, 0, GL30.GL_RGBA, this.width, this.height,
            0, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, this.buffer
        )
        GL30.glGenerateMipmap(GL30.GL_TEXTURE_2D)

        this.logger.debug("Uploaded texture from ${this.path}")
    }

    fun bind() = GL30.glBindTexture(GL30.GL_TEXTURE_2D, this.id)

    fun cleanup() = GL30.glDeleteTextures(this.id)
}

渲染器

package com.github.trqhxrd.untitledgame.engine.gui.rendering

import com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.ShaderProgram
import com.github.trqhxrd.untitledgame.engine.gui.window.Scene
import com.github.trqhxrd.untitledgame.engine.objects.GameObject
import com.github.trqhxrd.untitledgame.engine.objects.components.SpriteRenderer
import org.apache.logging.log4j.LogManager

class Renderer(val scene: Scene) {
    private val logger = LogManager.getLogger()

    companion object {
        const val MAX_BATCH_SIZE = 1024
    }

    private val batches = mutableListOf<RenderBatch>()

    fun add(obj: GameObject) {
        val sprite = obj.get(SpriteRenderer::class.java)
        if (sprite == null) {
            this.logger.warn("Tried to add GameObject '${obj.name}' to Renderer without a SpriteRenderer.")
            return
        }

        var added = false
        for (batch in this.batches) {
            if (batch.hasRoom) {
                batch.addSprite(sprite)
                added = true
                break
            }
        }

        if (!added) {
            val batch = RenderBatch(this.batches.size, this.scene, MAX_BATCH_SIZE)
            this.batches.add(batch)
            batch.addSprite(sprite)
        }
    }

    fun render() = this.batches.forEach { it.render() }
}

渲染批处理

package com.github.trqhxrd.untitledgame.engine.gui.rendering

import com.github.trqhxrd.untitledgame.engine.gui.window.DebugScene
import com.github.trqhxrd.untitledgame.engine.gui.window.Scene
import com.github.trqhxrd.untitledgame.engine.objects.components.SpriteRenderer
import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL30

class RenderBatch(val index: Int, val scene: Scene, val maxBatchSize: Int = 1024) {
    /*
     *  Vertex
     * --------
     * Pos              Color                           Texture
     * float, float,    float, float, float, float,     float, float
     */
    companion object {
        private const val POSITION_SIZE = 2
        private const val POSITION_OFFSET = 0
        private const val COLOR_SIZE = 4
        private const val COLOR_OFFSET = POSITION_OFFSET + POSITION_SIZE * Float.SIZE_BYTES
        private const val TEXTURE_SIZE = 2
        private const val TEXTURE_OFFSET = COLOR_OFFSET + TEXTURE_SIZE * Float.SIZE_BYTES
        private const val VERTEX_SIZE = POSITION_SIZE + COLOR_SIZE + TEXTURE_SIZE
        private const val VERTEX_SIZE_BYTES = VERTEX_SIZE * Float.SIZE_BYTES
    }

    private val logger = LogManager.getLogger()!!
    private val vertices = FloatArray(this.maxBatchSize * VERTEX_SIZE * 4) // 4 Vertices per sprite.
    private val sprites = arrayOfNulls<SpriteRenderer>(this.maxBatchSize)
    var numSprites = 0
        private set
    var hasRoom = true
        private set
    private var vao = -1
    private var vbo = -1

    init {
        this.logger.debug("Initializing new RenderBatch with max size of ${this.maxBatchSize} with ID #${this.index}!")
        this.scene.shader.validate()

        // Generate and bind VAO
        this.vao = GL30.glGenVertexArrays()
        GL30.glBindVertexArray(this.vao)

        // Allocate space for vertices
        this.vbo = GL30.glGenBuffers()
        GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, this.vbo)
        GL30.glBufferData(GL30.GL_ARRAY_BUFFER, this.vertices.size * Float.SIZE_BYTES.toLong(), GL30.GL_DYNAMIC_DRAW)

        // Create and upload indices buffer

        val ebo = GL30.glGenBuffers()
        val indices = this.generateIndices()
        GL30.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, ebo)
        GL30.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, indices, GL30.GL_STATIC_DRAW)

        // Enable the buffer attribute pointers
        GL30.glVertexAttribPointer(0, POSITION_SIZE, GL30.GL_FLOAT, false, VERTEX_SIZE_BYTES, POSITION_OFFSET.toLong())
        GL30.glEnableVertexAttribArray(0)
        GL30.glVertexAttribPointer(1, COLOR_SIZE, GL30.GL_FLOAT, false, VERTEX_SIZE_BYTES, COLOR_OFFSET.toLong())
        GL30.glEnableVertexAttribArray(1)
        GL30.glVertexAttribPointer(2, TEXTURE_SIZE, GL30.GL_FLOAT, false, VERTEX_SIZE_BYTES, TEXTURE_OFFSET.toLong())
        GL30.glEnableVertexAttribArray(2)
    }

    fun render() {
        GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, this.vbo)
        GL30.glBufferSubData(GL30.GL_ARRAY_BUFFER, 0, this.vertices)

        GL30.glActiveTexture(GL30.GL_TEXTURE0)
        GL30.glBindTexture(GL30.GL_TEXTURE_2D, DebugScene.texture.id)

        this.scene.uploadCameraDataToGPU()

        GL30.glBindVertexArray(this.vao)

        // Two triangles per square. Three indices per triangle.
        GL30.glDrawElements(GL30.GL_TRIANGLES, this.numSprites * 6, GL30.GL_UNSIGNED_INT, 0)

        GL30.glBindVertexArray(0)

        //     this.scene.shader.detach()
    }

    fun addSprite(sprite: SpriteRenderer): Boolean {
        if (!this.hasRoom) return false
        this.sprites[this.numSprites] = sprite
        this.loadVertexProperties(this.numSprites)

        this.logger.debug("Added new SpriteRenderer with index ${this.numSprites} to RenderBatch #${this.index}.")

        this.numSprites++

        if (this.numSprites >= this.maxBatchSize) this.hasRoom = false
        return true
    }

    private fun generateIndices(): IntArray {
        // Two triangles per square. Three indices per triangle.
        val elements = IntArray(this.maxBatchSize * 6)
        for (i in 0 until maxBatchSize) {
            val offsetIndex = 6 * i
            val offset = 4 * i

            // Element order: 3 2 0 0 2 1
            // Triangle 1
            elements[offsetIndex] = offset + 3
            elements[offsetIndex + 1] = offset + 2
            elements[offsetIndex + 2] = offset + 0

            // Triangle 2
            elements[offsetIndex + 3] = offset + 0
            elements[offsetIndex + 4] = offset + 2
            elements[offsetIndex + 5] = offset + 1
        }
        return elements
    }

    private fun loadVertexProperties(index: Int) {
        val sprite = this.sprites[index]!!
        // Find offset within array (4 vertices per sprite)
        var offset = index * 4 * VERTEX_SIZE

        var xAdd = 1f
        var yAdd = 1f
        for (i in 0..3) {
            when (i) {
                1 -> yAdd = 0f
                2 -> xAdd = 0f
                3 -> yAdd = 1f
                else -> {}
            }

            // Position
            this.vertices[offset] = sprite.obj!!.boundings.x + (xAdd * sprite.obj!!.boundings.width)
            this.vertices[offset + 1] = sprite.obj!!.boundings.y + (yAdd * sprite.obj!!.boundings.height)

            // Color
            this.vertices[offset + 2] = sprite.color.red
            this.vertices[offset + 3] = sprite.color.green
            this.vertices[offset + 4] = sprite.color.blue
            this.vertices[offset + 5] = sprite.color.alpha

            // Texture
            this.vertices[offset + 6] = xAdd
            this.vertices[offset + 7] = yAdd

            offset += VERTEX_SIZE
        }
    }
}

颜色

package com.github.trqhxrd.untitledgame.engine.gui.util

import java.util.concurrent.ThreadLocalRandom
import kotlin.math.min

class Color(red: Float, green: Float, blue: Float, alpha: Float) {

    val red: Float
    val green: Float
    val blue: Float
    val alpha: Float

    init {
        this.red = this.check(red)
        this.green = this.check(green)
        this.blue = this.check(blue)
        this.alpha = this.check(alpha)
    }

    companion object {
        val randomProvider = { ThreadLocalRandom.current() }
        fun random(): Color {
            return Color(
                this.randomProvider().nextFloat(),
                this.randomProvider().nextFloat(),
                this.randomProvider().nextFloat(),
                this.randomProvider().nextFloat()
            )
        }

        val WHITE = Color(1f, 1f, 1f, 1f)
        val GRAY = Color(.5f, .5f, .5f, 1f)
        val BLACK = Color(0f, 0f, 0f, 1f)
        val RED = Color(1f, 0f, 0f, 1f)
        val GREEN = Color(0f, 1f, 0f, 1f)
        val BLUE = Color(0f, 0f, 1f, 1f)
        val MAGENTA = Color(1f, 0f, 1f, 1f)
        val YELLOW = Color(1f, 1f, 0f, 1f)
        val CYAN = Color(0f, 1f, 1f, 1f)
    }

    constructor(color: Color) : this(color.red, color.green, color.blue, color.alpha)
    constructor(red: Float, green: Float, blue: Float) : this(red, green, blue, 1f)

    fun add(color: Color) = this.add(color.red, color.green, color.blue, color.alpha)

    fun add(red: Float = 0f, green: Float = 0f, blue: Float = 0f, alpha: Float = 0f) = Color(
        min(this.red + red, 1f),
        min(this.green + green, 1f),
        min(this.blue + blue, 1f),
        min(this.alpha + alpha, 1f)
    )

    private fun check(color: Float): Float {
        if (!(0f..1f).contains(color))
            throw IllegalArgumentException("Cannot assign color value smaller than 0 or greater than 1 ($color).")
        else return color
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as Color

        if (red != other.red) return false
        if (green != other.green) return false
        if (blue != other.blue) return false
        if (alpha != other.alpha) return false

        return true
    }

    override fun hashCode(): Int {
        var result = red.hashCode()
        result = 31 * result + green.hashCode()
        result = 31 * result + blue.hashCode()
        result = 31 * result + alpha.hashCode()
        return result
    }

    override fun toString(): String {
        return "Color(red=$red, green=$green, blue=$blue, alpha=$alpha)"
    }
}

着色器程序

package com.github.trqhxrd.untitledgame.engine.gui.rendering.shader

import com.github.trqhxrd.untitledgame.engine.exceptions.gui.ShaderCompilationException
import com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.types.FragmentShader
import com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.types.VertexShader
import org.apache.logging.log4j.LogManager
import org.joml.Matrix4f
import org.lwjgl.opengl.GL30
import org.lwjgl.system.MemoryUtil
import java.io.File

class ShaderProgram(val name: String) {
    var id = GL30.glCreateProgram()
        private set
    var vertex: VertexShader? = null
        private set
    var fragment: FragmentShader? = null
        private set
    private val logger = LogManager.getLogger()!!

    fun validate(): Boolean {
        val result = this.validateQuiet()
        return if (result != 0) {
            logger.warn(ShaderCompilationException("Shader validation for shader '${this.name}' failed ($result)."))
            false
        } else true
    }

    fun validateQuiet(): Int {
        var error = 0
        if (this.id == -1) error += 1
        if (this.vertex == null) error += 2
        if (this.fragment == null) error += 4
        return error
    }

    fun loadVertex(file: File) {
        this.vertex = VertexShader()
        this.vertex!!.create(file)
        this.vertex!!.attach(this)
    }

    fun loadFragment(file: File) {
        this.fragment = FragmentShader()
        this.fragment!!.create(file)
        this.fragment!!.attach(this)
    }

    fun link() = GL30.glLinkProgram(this.id)

    fun unlink() = GL30.glLinkProgram(0)

    fun use() = GL30.glUseProgram(this.id)

    fun detach() = GL30.glUseProgram(0)

    fun cleanup() {
        this.unlink()
        GL30.glDeleteProgram(this.id)
        this.id = -1
    }

    fun setUniform(varName: String, value: Matrix4f) {
        val varLoc = GL30.glGetUniformLocation(this.id, varName)
        val buf = MemoryUtil.memAllocFloat(4 * 4)
        value.get(buf)
        GL30.glUniformMatrix4fv(varLoc, false, buf)
    }

    fun setUniform(varName: String, value: Int) {
        val varLoc = GL30.glGetUniformLocation(this.id, varName)
        GL30.glUniform1i(varLoc, value)
    }
}

着色器

package com.github.trqhxrd.untitledgame.engine.gui.rendering.shader

import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL30
import java.io.File
import java.nio.charset.StandardCharsets

open class Shader(val type: ShaderType) {
    var id: Int = -1
        protected set
    var program: ShaderProgram? = null
        private set
    private val logger = LogManager.getLogger()!!

    fun create(file: File): Int {
        val src = file.readText(StandardCharsets.UTF_8)
        this.id = GL30.glCreateShader(this.type.glType)
        GL30.glShaderSource(this.id, src)
        GL30.glCompileShader(this.id)

        if (GL30.glGetShaderi(this.id, GL30.GL_COMPILE_STATUS) == 0)
            logger.error(
                "Failed to compile ${type.name.uppercase()}-SHADER. " +
                        "Error code: ${GL30.glGetShaderInfoLog(this.id)}"
            )

        return id
    }

    fun attach(program: ShaderProgram) {
        this.program = program
        GL30.glAttachShader(this.program!!.id, this.id)
    }

    fun detach() {
        if (this.program == null) return
        GL30.glDetachShader(this.program!!.id, this.id)
        this.program = null
    }
}

顶点着色器:(类别)

package com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.types

import com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.Shader
import com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.ShaderType

class VertexShader : Shader(ShaderType.VERTEX)

碎片着色器:(类别)

package com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.types

import com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.Shader
import com.github.trqhxrd.untitledgame.engine.gui.rendering.shader.ShaderType

class FragmentShader : Shader(ShaderType.FRAGMENT)

顶点着色器


# version 330

layout (location = 0) in vec2 aPosition;
layout (location = 1) in vec4 aColor;
layout (location = 2) in vec2 aTexture;

uniform mat4 uProjection;
uniform mat4 uView;

out vec4 fColor;
out vec2 fTexture;

void main() {
    fColor = aColor;
    fTexture = aTexture;
    gl_Position =  uProjection * uView * vec4(aPosition, 0.0, 1.0);
}

片段着色器


# version 330

in vec4 fColor;
in vec2 fTexture;

uniform sampler2D textureSampler;

out vec4 color;

void main() {
    color = texture2D(textureSampler, fTexture);
}

如果我缺少任何类的话,this(这已经是带有错误的提交)也是GitHub仓库。

7fyelxc5

7fyelxc51#

你看这个:

companion object {
    private const val POSITION_SIZE = 2
    private const val POSITION_OFFSET = 0
    private const val COLOR_SIZE = 4
    private const val COLOR_OFFSET = POSITION_OFFSET + POSITION_SIZE * Float.SIZE_BYTES
    private const val TEXTURE_SIZE = 2
    private const val TEXTURE_OFFSET = COLOR_OFFSET + TEXTURE_SIZE * Float.SIZE_BYTES
    private const val VERTEX_SIZE = POSITION_SIZE + COLOR_SIZE + TEXTURE_SIZE
    private const val VERTEX_SIZE_BYTES = VERTEX_SIZE * Float.SIZE_BYTES
}

你的TEXTURE_OFFSET完全错了,应该是

private const val TEXTURE_OFFSET = COLOR_OFFSET + COLOR_SIZE * Float.SIZE_BYTES
                                                  ^^^^^^^^^^

相关问题