EDIT:为了测试目的,我已经禁用了投影与视图矩阵,它现在可以工作了。我不知道出了什么问题,但我会调查的。
我对LWJGL比较陌生,目前正在尝试加载一个纹理。我可以看到纹理,但它只是一条垂直线,而不是四边形/正方形。我遵循了this和this教程。我的项目中有一个不同的结构,并试图移动函数,使其更适合我的结构。我检查了每个顶点的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仓库。
1条答案
按热度按时间7fyelxc51#
你看这个:
你的
TEXTURE_OFFSET
完全错了,应该是