我已经在 LWJGL 中下载了Nucklear 的演示程序,我已经设法编译它并让它成功运行。然后我尝试在我的游戏引擎中实现相同的代码并且没有任何显示,除非我禁用 glClear(GL_COLOR_BUFFER_BIT)。那时,我可以看到小核弹窗在闪烁。
这是我的窗口类
package Engine.Renderer;
import Engine.Messages.AppMsg;
import Engine.IntApplication;
import Engine.Messages.Type;
import Simulator.Application;
import org.lwjgl.nuklear.NkColorf;
import org.lwjgl.nuklear.NkMouse;
import org.lwjgl.opengl.*;
import org.lwjgl.system.Callback;
import org.lwjgl.system.MemoryStack;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.nuklear.Nuklear.*;
import static org.lwjgl.opengl.ARBDebugOutput.*;
import static org.lwjgl.opengl.ARBDebugOutput.GL_DEBUG_SEVERITY_LOW_ARB;
import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.*;
public class Window
{
public Window(int width, int height, String title)
{
m_Width = width;
m_Height = height;
m_Title = title;
if(!glfwInit())
{
///TODO: LOG
Runtime.getRuntime().exit(1);
}
glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
m_Window = glfwCreateWindow(m_Width, m_Height, m_Title, NULL, NULL);
if(m_Window == NULL)
{
///TODO: LOG
Runtime.getRuntime().exit(1);
}
glfwMakeContextCurrent(m_Window);
GLCapabilities caps = GL.createCapabilities();
glfwShowWindow(m_Window);
Callback debugProc = GLUtil.setupDebugMessageCallback();
if (caps.OpenGL43) {
GL43.glDebugMessageControl(GL43.GL_DEBUG_SOURCE_API, GL43.GL_DEBUG_TYPE_OTHER, GL43.GL_DEBUG_SEVERITY_NOTIFICATION, (IntBuffer)null, false);
} else if (caps.GL_KHR_debug) {
KHRDebug.glDebugMessageControl(
KHRDebug.GL_DEBUG_SOURCE_API,
KHRDebug.GL_DEBUG_TYPE_OTHER,
KHRDebug.GL_DEBUG_SEVERITY_NOTIFICATION,
(IntBuffer)null,
false
);
} else if (caps.GL_ARB_debug_output) {
glDebugMessageControlARB(GL_DEBUG_SOURCE_API_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DEBUG_SEVERITY_LOW_ARB, (IntBuffer)null, false);
}
SetUpWindow();
renderer = new GUIRenderer();
}
public void Draw()
{
}
public void processEvents()
{
try (MemoryStack stack = stackPush()) {
IntBuffer w = stack.mallocInt(1);
IntBuffer h = stack.mallocInt(1);
glfwGetWindowSize(m_Window, w, h);
m_Width = w.get(0);
m_Height = h.get(0);
glfwGetFramebufferSize(m_Window, w, h);
m_DisplayWidth = w.get(0);
m_DisplayHeight = h.get(0);
}
nk_input_begin(NuklearContainer.ctx);
glfwPollEvents();
NkMouse mouse = NuklearContainer.ctx.input().mouse();
if (mouse.grab()) {
glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
} else if (mouse.grabbed()) {
float prevX = mouse.prev().x();
float prevY = mouse.prev().y();
glfwSetCursorPos(m_Window, prevX, prevY);
mouse.pos().x(prevX);
mouse.pos().y(prevY);
} else if (mouse.ungrab()) {
glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
nk_input_end(NuklearContainer.ctx);
try (MemoryStack stack = stackPush()) {
IntBuffer width = stack.mallocInt(1);
IntBuffer height = stack.mallocInt(1);
glfwGetWindowSize(m_Window, width, height);
glViewport(0, 0, width.get(0), height.get(0));
NkColorf bg = NkColorf.create().r(0.10f).g(0.18f).b(0.24f).a(1.0f);
glClearColor(bg.r(), bg.g(), bg.b(), bg.a());
}
//glClear(GL_COLOR_BUFFER_BIT);
renderer.Render();
glfwSwapBuffers(m_Window);
}
public void CleanUp()
{
glfwFreeCallbacks(m_Window);
glfwDestroyWindow(m_Window);
glfwTerminate();
}
private void SetUpWindow()
{
glfwSetKeyCallback(m_Window, (window, key, scancode, action, mods) ->
{
AppMsg msg = new AppMsg();
msg.key = key;
msg.scancode = scancode;
msg.mods = mods;
if(action == 0)
msg.type = Type.KEYUP;
if(action == 1)
msg.type = Type.KEYDOWN;
if(action == 2)
msg.type = Type.KEYREPEAT;
IntApplication.OnMsgProc(msg);
});
glfwSetMouseButtonCallback(m_Window, (window, button, action, mods) ->
{
AppMsg msg = new AppMsg();
msg.button = button;
msg.mods = mods;
if(action == 0)
msg.type = Type.MOUSEUP;
if(action == 1)
msg.type = Type.MOUSEDOWN;
Application.OnMsgProc(msg);
});
glfwSetCursorPosCallback(m_Window, (window, xpos, ypos) ->
{
AppMsg msg = new AppMsg();
msg.xpos = xpos;
msg.ypos = ypos;
msg.type = Type.MOUSEMOVE;
Application.OnMsgProc(msg);
});
glfwSetWindowCloseCallback(m_Window, (window) ->
IntApplication.OnClose());
nk_init(NuklearContainer.ctx, NuklearContainer.ALLOCATOR, null);
NuklearContainer.ctx.clip()
.copy((handle, text, len) -> {
if (len == 0) {
return;
}
try (MemoryStack stack = stackPush()) {
ByteBuffer str = stack.malloc(len + 1);
memCopy(text, memAddress(str), len);
str.put(len, (byte)0);
glfwSetClipboardString(m_Window, str);
}
})
.paste((handle, edit) -> {
long text = nglfwGetClipboardString(m_Window);
if (text != NULL) {
nnk_textedit_paste(edit, text, nnk_strlen(text));
}
});
}
private long m_Window;
static int m_Width, m_Height;
static int m_DisplayWidth, m_DisplayHeight;
private String m_Title;
GUIRenderer renderer;
}
这是我的 GUIRender 类
package Engine.Renderer;
import static org.lwjgl.nuklear.Nuklear.*;
import static org.lwjgl.opengl.GL20C.*;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import static org.lwjgl.stb.STBTruetype.*;
import static org.lwjgl.stb.STBTruetype.stbtt_GetCodepointHMetrics;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.*;
import static org.lwjgl.system.MemoryUtil.memAddress;
import Engine.IOUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Objects;
import org.lwjgl.nuklear.*;
import org.lwjgl.stb.STBTTAlignedQuad;
import org.lwjgl.stb.STBTTFontinfo;
import org.lwjgl.stb.STBTTPackContext;
import org.lwjgl.stb.STBTTPackedchar;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.Platform;
public class GUIRenderer extends Renderer
{
public GUIRenderer()
{
super();
try
{
this.ttf = IOUtil.ioResourceToByteBuffer("C:/Windows/Fonts/Arial.ttf", 512 * 1024);
} catch (IOException e)
{
throw new RuntimeException(e);
}
if (!m_Initialized)
Initialize();
try (MemoryStack stack = stackPush()) {
NkRect rect = NkRect.mallocStack(stack);
if (nk_begin(
NuklearContainer.ctx,
"Hello World",
nk_rect(50, 50, 230, 250, rect),
NK_WINDOW_BORDER | NK_WINDOW_NO_INPUT | NK_WINDOW_NO_INPUT | NK_WINDOW_NO_INPUT | NK_WINDOW_TITLE
)) {
nk_layout_row_static(NuklearContainer.ctx, 20, 80, 1);
nk_label(NuklearContainer.ctx, "background:", NK_TEXT_LEFT);
}
}
nk_end(NuklearContainer.ctx);
}
private void Initialize()
{
String NK_SHADER_VERSION = Platform.get() == Platform.MACOSX ? "#version 150\n" : "#version 300 es\n";
String vertex_shader =
NK_SHADER_VERSION +
"uniform mat4 ProjMtx;\n" +
"in vec2 Position;\n" +
"in vec2 TexCoord;\n" +
"in vec4 Color;\n" +
"out vec2 Frag_UV;\n" +
"out vec4 Frag_Color;\n" +
"void main() {\n" +
" Frag_UV = TexCoord;\n" +
" Frag_Color = Color;\n" +
" gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" +
"}\n";
String fragment_shader =
NK_SHADER_VERSION +
"precision mediump float;\n" +
"uniform sampler2D Texture;\n" +
"in vec2 Frag_UV;\n" +
"in vec4 Frag_Color;\n" +
"out vec4 Out_Color;\n" +
"void main(){\n" +
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" +
"}\n";
nk_buffer_init(NuklearContainer.cmds, NuklearContainer.ALLOCATOR, BUFFER_INITIAL_SIZE);
m_Program = glCreateProgram();
m_Vertex_Shader = glCreateShader(GL_VERTEX_SHADER);
m_Fragment_Shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(m_Vertex_Shader, vertex_shader);
glShaderSource(m_Fragment_Shader, fragment_shader);
glCompileShader(m_Vertex_Shader);
glCompileShader(m_Fragment_Shader);
if (glGetShaderi(m_Vertex_Shader, GL_COMPILE_STATUS) != GL_TRUE)
{
throw new IllegalStateException();
}
if (glGetShaderi(m_Fragment_Shader, GL_COMPILE_STATUS) != GL_TRUE)
{
throw new IllegalStateException();
}
glAttachShader(m_Program, m_Vertex_Shader);
glAttachShader(m_Program, m_Fragment_Shader);
glLinkProgram(m_Program);
if (glGetProgrami(m_Program, GL_LINK_STATUS) != GL_TRUE)
{
throw new IllegalStateException();
}
m_Uniform_Texture = glGetUniformLocation(m_Program, "Texture");
m_Uniform_Proj = glGetUniformLocation(m_Program, "ProjMtx");
int attrib_pos = glGetAttribLocation(m_Program, "Position");
int attrib_uv = glGetAttribLocation(m_Program, "TexCoord");
int attrib_col = glGetAttribLocation(m_Program, "Color");
{
// buffer setup
m_Vbo = glGenBuffers();
m_Ebo = glGenBuffers();
m_Vao = glGenVertexArrays();
glBindVertexArray(m_Vao);
glBindBuffer(GL_ARRAY_BUFFER, m_Vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Ebo);
glEnableVertexAttribArray(attrib_pos);
glEnableVertexAttribArray(attrib_uv);
glEnableVertexAttribArray(attrib_col);
glVertexAttribPointer(attrib_pos, 2, GL_FLOAT, false, 20, 0);
glVertexAttribPointer(attrib_uv, 2, GL_FLOAT, false, 20, 8);
glVertexAttribPointer(attrib_col, 4, GL_UNSIGNED_BYTE, true, 20, 16);
}
{
// null texture setup
int nullTexID = glGenTextures();
NuklearContainer.null_texture.texture().id(nullTexID);
NuklearContainer.null_texture.uv().set(0.5f, 0.5f);
glBindTexture(GL_TEXTURE_2D, nullTexID);
try (MemoryStack stack = stackPush())
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, stack.ints(0xFFFFFFFF));
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
FontInit();
}
private void FontInit()
{
int BITMAP_W = 1024;
int BITMAP_H = 1024;
int FONT_HEIGHT = 18;
int fontTexID = glGenTextures();
STBTTFontinfo fontInfo = STBTTFontinfo.create();
STBTTPackedchar.Buffer cdata = STBTTPackedchar.create(95);
float scale;
float descent;
try (MemoryStack stack = stackPush()) {
stbtt_InitFont(fontInfo, ttf);
scale = stbtt_ScaleForPixelHeight(fontInfo, FONT_HEIGHT);
IntBuffer d = stack.mallocInt(1);
stbtt_GetFontVMetrics(fontInfo, null, d, null);
descent = d.get(0) * scale;
ByteBuffer bitmap = memAlloc(BITMAP_W * BITMAP_H);
STBTTPackContext pc = STBTTPackContext.mallocStack(stack);
stbtt_PackBegin(pc, bitmap, BITMAP_W, BITMAP_H, 0, 1, NULL);
stbtt_PackSetOversampling(pc, 4, 4);
stbtt_PackFontRange(pc, ttf, 0, FONT_HEIGHT, 32, cdata);
stbtt_PackEnd(pc);
// Convert R8 to RGBA8
ByteBuffer texture = memAlloc(BITMAP_W * BITMAP_H * 4);
for (int i = 0; i < bitmap.capacity(); i++) {
texture.putInt((bitmap.get(i) << 24) | 0x00FFFFFF);
}
texture.flip();
glBindTexture(GL_TEXTURE_2D, fontTexID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, BITMAP_W, BITMAP_H, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
memFree(texture);
memFree(bitmap);
}
NuklearContainer.default_font
.width((handle, h, text, len) -> {
float text_width = 0;
try (MemoryStack stack = stackPush()) {
IntBuffer unicode = stack.mallocInt(1);
int glyph_len = nnk_utf_decode(text, memAddress(unicode), len);
int text_len = glyph_len;
if (glyph_len == 0) {
return 0;
}
IntBuffer advance = stack.mallocInt(1);
while (text_len <= len && glyph_len != 0) {
if (unicode.get(0) == NK_UTF_INVALID) {
break;
}
/* query currently drawn glyph information */
stbtt_GetCodepointHMetrics(fontInfo, unicode.get(0), advance, null);
text_width += advance.get(0) * scale;
/* offset next glyph */
glyph_len = nnk_utf_decode(text + text_len, memAddress(unicode), len - text_len);
text_len += glyph_len;
}
}
return text_width;
})
.height(FONT_HEIGHT)
.query((handle, font_height, glyph, codepoint, next_codepoint) -> {
try (MemoryStack stack = stackPush()) {
FloatBuffer x = stack.floats(0.0f);
FloatBuffer y = stack.floats(0.0f);
STBTTAlignedQuad q = STBTTAlignedQuad.mallocStack(stack);
IntBuffer advance = stack.mallocInt(1);
stbtt_GetPackedQuad(cdata, BITMAP_W, BITMAP_H, codepoint - 32, x, y, q, false);
stbtt_GetCodepointHMetrics(fontInfo, codepoint, advance, null);
NkUserFontGlyph ufg = NkUserFontGlyph.create(glyph);
ufg.width(q.x1() - q.x0());
ufg.height(q.y1() - q.y0());
ufg.offset().set(q.x0(), q.y0() + (FONT_HEIGHT + descent));
ufg.xadvance(advance.get(0) * scale);
ufg.uv(0).set(q.s0(), q.t0());
ufg.uv(1).set(q.s1(), q.t1());
}
})
.texture(it -> it
.id(fontTexID));
nk_style_set_font(NuklearContainer.ctx, NuklearContainer.default_font);
}
public void Render()
{
try (MemoryStack stack = stackPush())
{
// setup global state
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
glActiveTexture(GL_TEXTURE0);
// setup program
glUseProgram(m_Program);
glUniform1i(m_Uniform_Texture, 0);
glUniformMatrix4fv(m_Uniform_Proj, false, stack.floats(
2.0f / Window.m_Width, 0.0f, 0.0f, 0.0f,
0.0f, -2.0f / Window.m_Height, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f
));
glViewport(0, 0, Window.m_DisplayWidth, Window.m_DisplayHeight);
}
{
// convert from command queue into draw list and draw to screen
// allocate vertex and element buffer
glBindVertexArray(m_Vao);
glBindBuffer(GL_ARRAY_BUFFER, m_Vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Ebo);
glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, GL_STREAM_DRAW);
// load draw vertices & elements directly into vertex + element buffer
ByteBuffer vertices = Objects.requireNonNull(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY, max_vertex_buffer, null));
ByteBuffer elements = Objects.requireNonNull(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY, max_element_buffer, null));
try (MemoryStack stack = stackPush())
{
// fill convert configuration
NkConvertConfig config = NkConvertConfig.callocStack(stack)
.vertex_layout(VERTEX_LAYOUT)
.vertex_size(20)
.vertex_alignment(4)
.null_texture(NuklearContainer.null_texture)
.circle_segment_count(22)
.curve_segment_count(22)
.arc_segment_count(22)
.global_alpha(1.0f)
.shape_AA(NK_ANTI_ALIASING_ON)
.line_AA(NK_ANTI_ALIASING_ON);
// setup buffers to load vertices and elements
NkBuffer vbuf = NkBuffer.mallocStack(stack);
NkBuffer ebuf = NkBuffer.mallocStack(stack);
nk_buffer_init_fixed(vbuf, vertices/*, max_vertex_buffer*/);
nk_buffer_init_fixed(ebuf, elements/*, max_element_buffer*/);
nk_convert(NuklearContainer.ctx, NuklearContainer.cmds, vbuf, ebuf, config);
}
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
glUnmapBuffer(GL_ARRAY_BUFFER);
// iterate over and execute each draw command
float fb_scale_x = (float) Window.m_DisplayWidth / (float) Window.m_Width;
float fb_scale_y = (float) Window.m_DisplayHeight / (float) Window.m_Height;
long offset = NULL;
for (NkDrawCommand cmd = nk__draw_begin(NuklearContainer.ctx, NuklearContainer.cmds); cmd != null; cmd = nk__draw_next(cmd, NuklearContainer.cmds, NuklearContainer.ctx))
{
if (cmd.elem_count() == 0)
{
continue;
}
glBindTexture(GL_TEXTURE_2D, cmd.texture().id());
glScissor(
(int) (cmd.clip_rect().x() * fb_scale_x),
(int) ((Window.m_Height - (int) (cmd.clip_rect().y() + cmd.clip_rect().h())) * fb_scale_y),
(int) (cmd.clip_rect().w() * fb_scale_x),
(int) (cmd.clip_rect().h() * fb_scale_y)
);
glDrawElements(GL_TRIANGLES, cmd.elem_count(), GL_UNSIGNED_SHORT, offset);
offset += cmd.elem_count() * 2;
}
nk_clear(NuklearContainer.ctx);
}
glUseProgram(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glDisable(GL_BLEND);
glDisable(GL_SCISSOR_TEST);
}
private boolean m_Initialized;
public boolean IsInitialized() { return m_Initialized; }
private int BUFFER_INITIAL_SIZE = 4 * 1024;
long max_vertex_buffer = 512 * 1024;
long max_element_buffer = 128 * 1024;
private NkDrawVertexLayoutElement.Buffer VERTEX_LAYOUT = NkDrawVertexLayoutElement.create(4)
.position(0).attribute(NK_VERTEX_POSITION).format(NK_FORMAT_FLOAT).offset(0)
.position(1).attribute(NK_VERTEX_TEXCOORD).format(NK_FORMAT_FLOAT).offset(8)
.position(2).attribute(NK_VERTEX_COLOR).format(NK_FORMAT_R8G8B8A8).offset(16)
.position(3).attribute(NK_VERTEX_ATTRIBUTE_COUNT).format(NK_FORMAT_COUNT).offset(0)
.flip();
private ByteBuffer ttf;
}
如果代码很大,我真的很抱歉,但即使只是 GLFWDemo 也有 600 多行。