mirror of
https://github.com/Minestom/Minestom.git
synced 2025-03-02 11:21:15 +01:00
Drop-in palette lookup post-processing
Directly usable for devs
This commit is contained in:
parent
55b36624cc
commit
adf34b4742
src/lwjgl
java/net/minestom
resources/shaders
@ -16,6 +16,7 @@ import net.minestom.server.map.MapColors;
|
||||
import net.minestom.server.map.framebuffers.LargeDirectFramebuffer;
|
||||
import net.minestom.server.map.framebuffers.LargeGLFWFramebuffer;
|
||||
import net.minestom.server.map.framebuffers.LargeGraphics2DFramebuffer;
|
||||
import net.minestom.server.map.framebuffers.MapColorRenderer;
|
||||
import net.minestom.server.network.packet.server.play.MapDataPacket;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
@ -43,17 +44,16 @@ public class Demo {
|
||||
LargeGraphics2DFramebuffer graphics2DFramebuffer = new LargeGraphics2DFramebuffer(512, 512);
|
||||
LargeGLFWFramebuffer glfwFramebuffer = new LargeGLFWFramebuffer(512, 512);
|
||||
|
||||
glfwFramebuffer.useMapColors();
|
||||
|
||||
glfwFramebuffer.changeRenderingThreadToCurrent();
|
||||
OpenGLRendering.init();
|
||||
MapColorRenderer renderer = new MapColorRenderer(glfwFramebuffer, OpenGLRendering::render);
|
||||
glfwFramebuffer.unbindContextFromThread();
|
||||
|
||||
// renderingLoop(0, directFramebuffer, Demo::directRendering);
|
||||
// renderingLoop(101, graphics2DFramebuffer, Demo::graphics2DRendering);
|
||||
renderingLoop(201, glfwFramebuffer, f -> {});
|
||||
|
||||
glfwFramebuffer.setupRenderLoop(15, TimeUnit.MILLISECOND, () -> OpenGLRendering.render(glfwFramebuffer));
|
||||
glfwFramebuffer.setupRenderLoop(15, TimeUnit.MILLISECOND, renderer);
|
||||
|
||||
for (int x = -2; x <= 2; x++) {
|
||||
for (int z = -2; z <= 2; z++) {
|
||||
|
@ -1,14 +1,14 @@
|
||||
package net.minestom.demo.largeframebuffers;
|
||||
|
||||
import net.minestom.server.map.MapColors;
|
||||
import net.minestom.server.map.framebuffers.LargeGLFWFramebuffer;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GLUtil;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -64,13 +64,11 @@ public final class OpenGLRendering {
|
||||
private static int projectionUniform;
|
||||
private static int viewUniform;
|
||||
private static int modelUniform;
|
||||
private static int paletteTexture;
|
||||
private static int boxTexture;
|
||||
|
||||
static void init() {
|
||||
GLUtil.setupDebugMessageCallback();
|
||||
|
||||
paletteTexture = loadTexture("palette");
|
||||
boxTexture = loadTexture("box");
|
||||
|
||||
vbo = glGenBuffers();
|
||||
@ -81,15 +79,6 @@ public final class OpenGLRendering {
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, false, VERTEX_SIZE, 0); // position
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, false, VERTEX_SIZE, 3*4); // color
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// prepare matrices and shader
|
||||
renderShader = glCreateProgram();
|
||||
projectionMatrix = new Matrix4f().setPerspective((float) (Math.PI/4f), 1f, 0.001f, 100f);
|
||||
@ -107,8 +96,6 @@ public final class OpenGLRendering {
|
||||
projectionUniform = glGetUniformLocation(renderShader, "projection");
|
||||
viewUniform = glGetUniformLocation(renderShader, "view");
|
||||
modelUniform = glGetUniformLocation(renderShader, "model");
|
||||
int paletteSizeUniform = glGetUniformLocation(renderShader, "paletteSize");
|
||||
int paletteUniform = glGetUniformLocation(renderShader, "palette");
|
||||
int boxUniform = glGetUniformLocation(renderShader, "box");
|
||||
|
||||
glUseProgram(renderShader); {
|
||||
@ -116,9 +103,8 @@ public final class OpenGLRendering {
|
||||
uploadMatrix(viewUniform, viewMatrix);
|
||||
|
||||
glUniform1i(boxUniform, 0); // texture unit 0
|
||||
glUniform1i(paletteUniform, 1); // texture unit 1
|
||||
glUniform1f(paletteSizeUniform, 236);
|
||||
}
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
private static int loadTexture(String filename) {
|
||||
@ -166,7 +152,6 @@ public final class OpenGLRendering {
|
||||
private static int createShader(String filename, int type) {
|
||||
int shader = glCreateShader(type);
|
||||
try(BufferedReader reader = new BufferedReader(new InputStreamReader(OpenGLRendering.class.getResourceAsStream(filename)))) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
String source = reader.lines().collect(Collectors.joining("\n"));
|
||||
glShaderSource(shader, source);
|
||||
glCompileShader(shader);
|
||||
@ -180,7 +165,7 @@ public final class OpenGLRendering {
|
||||
|
||||
private static int frame = 0;
|
||||
|
||||
static void render(LargeGLFWFramebuffer framebuffer) {
|
||||
static void render() {
|
||||
if(frame % 100 == 0) {
|
||||
long time = System.currentTimeMillis();
|
||||
long dt = time-lastTime;
|
||||
@ -192,23 +177,24 @@ public final class OpenGLRendering {
|
||||
frame++;
|
||||
|
||||
|
||||
glClearColor(MapColors.COLOR_BLACK.multiply53()/255.0f, 0f, 0f, 1f);
|
||||
glClearColor(0f, 0f, 0f, 1f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
modelMatrix.rotateY((float) (Math.PI/60f));
|
||||
|
||||
// TODO: render texture
|
||||
glUseProgram(renderShader); {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, paletteTexture);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, boxTexture);
|
||||
|
||||
uploadMatrix(modelUniform, modelMatrix);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, false, VERTEX_SIZE, 0); // position
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, false, VERTEX_SIZE, 3*4); // color
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
|
@ -0,0 +1,257 @@
|
||||
package net.minestom.server.map.framebuffers;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
|
||||
/**
|
||||
* Helper class designed to help OpenGL users to convert their RGB values to map colors inside a post processing pass
|
||||
* with a shader provided by Minestom.
|
||||
*
|
||||
* When rendering to a {@link GLFWFramebuffer} or a {@link LargeGLFWFramebuffer}, wrap your rendering in a MapColorRenderer to render to the GLFW with map colors.
|
||||
*
|
||||
* {@link MapColorRenderer} sets up an OpenGL framebuffer with the size of the underlying framebuffer and renders to it.
|
||||
* The initialization of the framebuffer is done in the constructor.
|
||||
* Therefore, the constructor call should be done inside the thread linked to the OpenGL context. The context can
|
||||
* be moved through {@link GLFWCapableBuffer#changeRenderingThreadToCurrent()} and {@link GLFWCapableBuffer#unbindContextFromThread()}
|
||||
*
|
||||
* <hr/>
|
||||
* Resources created in constructor are:
|
||||
* <li>
|
||||
* <ul>Framebuffer</ul>
|
||||
* <ul>Color texture (if default fbo initialization chosen)</ul>
|
||||
* <ul>Depth24 Stencil8 render buffer (if default fbo initialization chosen)</ul>
|
||||
* <ul>Post processing shader program</ul>
|
||||
* <ul>Palette texture</ul>
|
||||
* <ul>Screen quad VAO</ul>
|
||||
* <ul>Screen quad index buffer</ul>
|
||||
* </li>
|
||||
*
|
||||
* The constructor also puts the given buffer in map color mode.
|
||||
*/
|
||||
public class MapColorRenderer implements Runnable {
|
||||
|
||||
private final int fboID;
|
||||
private final GLFWCapableBuffer framebuffer;
|
||||
private final Runnable renderCode;
|
||||
private final int colorTextureID;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int renderShader;
|
||||
private final int screenQuadIndices;
|
||||
private int paletteTexture;
|
||||
private float paletteSize;
|
||||
private final int screenQuadVAO;
|
||||
|
||||
public MapColorRenderer(GLFWCapableBuffer framebuffer, Runnable renderCode) {
|
||||
this(framebuffer, renderCode, MapColorRenderer.defaultFramebuffer(framebuffer.width(), framebuffer.height()));
|
||||
}
|
||||
|
||||
public MapColorRenderer(GLFWCapableBuffer framebuffer, Runnable renderCode, FboInitialization fboInitialization) {
|
||||
this.framebuffer = framebuffer;
|
||||
this.framebuffer.useMapColors();
|
||||
|
||||
this.renderCode = renderCode;
|
||||
this.width = framebuffer.width();
|
||||
this.height = framebuffer.height();
|
||||
|
||||
this.fboID = glGenFramebuffers();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fboID);
|
||||
this.colorTextureID = fboInitialization.initFbo(fboID);
|
||||
|
||||
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
throw new RuntimeException("Framebuffer is not complete!");
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
// create post-process shader
|
||||
this.renderShader = glCreateProgram();
|
||||
int vertexShader = createShader("/shaders/mapcolorconvert.vertex.glsl", GL_VERTEX_SHADER);
|
||||
int fragmentShader = createShader("/shaders/mapcolorconvert.fragment.glsl", GL_FRAGMENT_SHADER);
|
||||
glAttachShader(renderShader, vertexShader);
|
||||
glAttachShader(renderShader, fragmentShader);
|
||||
glLinkProgram(renderShader);
|
||||
if(glGetProgrami(renderShader, GL_LINK_STATUS) == 0) {
|
||||
throw new RuntimeException("Link error: "+glGetProgramInfoLog(renderShader));
|
||||
}
|
||||
|
||||
loadPalette("palette");
|
||||
|
||||
// create screen quad VAO
|
||||
screenQuadVAO = glGenBuffers();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, screenQuadVAO);
|
||||
glBufferData(GL_ARRAY_BUFFER, new float[] {
|
||||
-1f, -1f,
|
||||
1f, -1f,
|
||||
1f, 1f,
|
||||
-1f, 1f
|
||||
}, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
screenQuadIndices = glGenBuffers();
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screenQuadIndices);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, new int[] {0,1,2, 2,3,0}, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
int paletteSizeUniform = glGetUniformLocation(renderShader, "paletteSize");
|
||||
int paletteUniform = glGetUniformLocation(renderShader, "palette");
|
||||
int frameUniform = glGetUniformLocation(renderShader, "frame");
|
||||
|
||||
glUseProgram(renderShader); {
|
||||
glUniform1i(frameUniform, 0); // texture unit 0
|
||||
glUniform1i(paletteUniform, 1); // texture unit 1
|
||||
glUniform1f(paletteSizeUniform, paletteSize);
|
||||
}
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
private static FboInitialization defaultFramebuffer(int width, int height) {
|
||||
return fboId -> defaultFramebufferInit(fboId, width, height);
|
||||
}
|
||||
|
||||
private static int defaultFramebufferInit(int fbo, int width, int height) {
|
||||
// color
|
||||
int colorTexture = glGenTextures();
|
||||
glBindTexture(GL_TEXTURE_2D, colorTexture);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0L);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
// attach to framebuffer
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// depth
|
||||
int depthStencilBuffer = glGenRenderbuffers();
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
return colorTexture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
glViewport(0, 0, width, height);
|
||||
// run user code inside of framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fboID);
|
||||
renderCode.run();
|
||||
|
||||
// run post processing to display on screen
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glClearColor(0f, 0f, 0f, 1f); // 0 on RED channel makes maps use NONE
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, colorTextureID);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, paletteTexture);
|
||||
|
||||
glUseProgram(renderShader); {
|
||||
// render post processing quad
|
||||
glBindBuffer(GL_ARRAY_BUFFER, screenQuadVAO);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, false, 2*4, 0); // position
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screenQuadIndices);
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||
};
|
||||
|
||||
glUseProgram(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees OpenGL resources used by this renderer.
|
||||
* You should NOT render with this renderer after this call.
|
||||
*/
|
||||
public void cleanupResources() {
|
||||
glDeleteFramebuffers(fboID);
|
||||
glDeleteProgram(renderShader);
|
||||
glDeleteTextures(paletteTexture);
|
||||
// TODO: more cleanup
|
||||
}
|
||||
|
||||
private void loadPalette(String filename) {
|
||||
int tex = glGenTextures();
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
BufferedImage image;
|
||||
try {
|
||||
image = ImageIO.read(MapColorRenderer.class.getResourceAsStream("/textures/"+filename+".png"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Missing image "+filename, e);
|
||||
}
|
||||
ByteBuffer pixels = BufferUtils.createByteBuffer(image.getWidth()*image.getHeight()*4);
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
int rgb = image.getRGB(x, y);
|
||||
int alpha = (rgb >> 24) & 0xFF;
|
||||
int red = (rgb >> 16) & 0xFF;
|
||||
int green = (rgb >> 8) & 0xFF;
|
||||
int blue = rgb & 0xFF;
|
||||
pixels.put((byte) red);
|
||||
pixels.put((byte) green);
|
||||
pixels.put((byte) blue);
|
||||
pixels.put((byte) alpha);
|
||||
}
|
||||
}
|
||||
pixels.flip();
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
// closest neighbor required here, as pixels can have very different rgb values, and interpolation will break palette lookup
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
this.paletteTexture = tex;
|
||||
this.paletteSize = image.getWidth();
|
||||
}
|
||||
|
||||
private static int createShader(String filename, int type) {
|
||||
int shader = glCreateShader(type);
|
||||
try(BufferedReader reader = new BufferedReader(new InputStreamReader(MapColorRenderer.class.getResourceAsStream(filename)))) {
|
||||
String source = reader.lines().collect(Collectors.joining("\n"));
|
||||
glShaderSource(shader, source);
|
||||
glCompileShader(shader);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface FboInitialization {
|
||||
|
||||
/**
|
||||
* Initializes the given framebuffer
|
||||
* @param fboId
|
||||
* @return the texture ID of the color texture, used for post processing.
|
||||
*/
|
||||
int initFbo(int fboId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,29 +5,8 @@ in vec2 uv;
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D box;
|
||||
uniform sampler2D palette;
|
||||
uniform float paletteSize;
|
||||
|
||||
void main() {
|
||||
vec3 vertexColor = texture(box, uv).rgb;
|
||||
|
||||
|
||||
// render in map colors
|
||||
int closest = 0;
|
||||
uint closestDistance = uint(2147483647);
|
||||
for(int i = 4; i < paletteSize; i++) {
|
||||
vec3 mapColor = texture(palette, vec2((i+0.5f)/paletteSize, 0.0)).rgb;
|
||||
int dr = int((mapColor.r - vertexColor.r)*255);
|
||||
int dg = int((mapColor.g - vertexColor.g)*255);
|
||||
int db = int((mapColor.b - vertexColor.b)*255);
|
||||
|
||||
uint d = uint(dr*dr)+uint(dg*dg)+uint(db*db);
|
||||
if(d < closestDistance) {
|
||||
closestDistance = d;
|
||||
closest = i;
|
||||
}
|
||||
}
|
||||
|
||||
fragColor = vec4(closest/255.0, closest/255.0, closest/255.0, 1.0);
|
||||
//fragColor = vec4(vertexColor, 1.0);
|
||||
fragColor = vec4(vertexColor, 1.0);
|
||||
}
|
30
src/lwjgl/resources/shaders/mapcolorconvert.fragment.glsl
Normal file
30
src/lwjgl/resources/shaders/mapcolorconvert.fragment.glsl
Normal file
@ -0,0 +1,30 @@
|
||||
#version 330
|
||||
|
||||
in vec2 fragCoords;
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D frame;
|
||||
uniform sampler2D palette;
|
||||
uniform float paletteSize;
|
||||
|
||||
void main() {
|
||||
vec3 fragmentColor = texture(frame, fragCoords).rgb;
|
||||
|
||||
// render in map colors
|
||||
int closest = 0;
|
||||
uint closestDistance = uint(2147483647);
|
||||
for(int i = 4; i < paletteSize; i++) {
|
||||
vec3 mapColor = texture(palette, vec2((i+0.5f)/paletteSize, 0.0)).rgb;
|
||||
int dr = int((mapColor.r - fragmentColor.r)*255);
|
||||
int dg = int((mapColor.g - fragmentColor.g)*255);
|
||||
int db = int((mapColor.b - fragmentColor.b)*255);
|
||||
|
||||
uint d = uint(dr*dr)+uint(dg*dg)+uint(db*db);
|
||||
if(d < closestDistance) {
|
||||
closestDistance = d;
|
||||
closest = i;
|
||||
}
|
||||
}
|
||||
|
||||
fragColor = vec4(closest/255.0, closest/255.0, closest/255.0, 1.0);
|
||||
}
|
10
src/lwjgl/resources/shaders/mapcolorconvert.vertex.glsl
Normal file
10
src/lwjgl/resources/shaders/mapcolorconvert.vertex.glsl
Normal file
@ -0,0 +1,10 @@
|
||||
#version 330
|
||||
|
||||
layout(location = 0) in vec2 pos;
|
||||
|
||||
out vec2 fragCoords;
|
||||
|
||||
void main() {
|
||||
fragCoords = (pos+vec2(1.0))/2.0;
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user