I am trying to follow LearnOpenGL while coding in the Zig language, and something that is very odd is that sometimes my shader compilation fails even though I changed nothing between executing the app. I also don't understand the errors.
By the way, I use mach-glfw
and zigglgen
.
Some errors I get:
error: [opengl] Failed to compile shader: assets/shaders/shader.frag
0(8) : error C0000: syntax error, unexpected '=' at token "="
error: [opengl] Failed to link shader program:
Fragment info
-------------
0(8) : error C0000: syntax error, unexpected '=' at token "="
(0) : error C2003: incompatible options for link
error: [opengl] Failed to compile shader: assets/shaders/shader.frag
0(9) : error C0000: syntax error, unexpected '(', expecting ';' at token "("
error: [opengl] Failed to link shader program:
Fragment info
-------------
0(9) : error C0000: syntax error, unexpected '(', expecting ';' at token "("
(0) : error C2003: incompatible options for link
error: [opengl] Failed to compile shaders/shader.vert:
0(6) : error C0000: syntax error, unexpected $undefined at token "<undefined>"
Here is the code:
// Vertex shader
#version 330 core
layout (location = 0) in vec3 aPos;
out vec4 vertexColor;
void main() {
gl_Position = vec4(aPos.xyz, 1.0);
vertexColor = vec4(0.5, 0.0, 0.0, 1.0);
}
// Fragment shader
#version 330 core
out vec4 FragColor;
in vec4 vertexColor;
void main() {
FragColor = vertexColor;
}
// Shortened main code
const std = @import("std");
const builtin = @import("builtin");
const glfw = @import("glfw");
const gl = @import("gl");
const App = @import("App.zig");
var gl_procs: gl.ProcTable = undefined;
fn glfwErrorCallback(err: glfw.ErrorCode, desc: [:0]const u8) void {
...
}
fn glfwFramebufferSizeCallback(_: glfw.Window, w: u32, h: u32) void {
gl.Viewport(0, 0, @intCast(w), @intCast(h));
}
pub fn main() !void {
// GLFW initialization
if (!glfw.init(.{})) {
...
}
defer glfw.terminate();
// Window creation
const window = glfw.Window.create(1280, 720, "example opengl app", null, null, .{
...
}) orelse {
...
};
defer window.destroy();
glfw.makeContextCurrent(window);
// OpenGL preparation
if (!gl_procs.init(glfw.getProcAddress)) {
...
}
gl.makeProcTableCurrent(&gl_procs);
window.setFramebufferSizeCallback(glfwFramebufferSizeCallback);
// App startup
var app = App{
.window = window,
};
app.run() catch |err| {
...
};
}
// shortened App code
...
window: glfw.Window,
vertices: [12]f32 = [_]f32{
0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0,
-0.5, 0.5, 0.0,
},
indices: [6]gl.uint = [_]gl.uint{
0, 1, 3,
1, 2, 3,
},
fn createCompiledShader(file: []const u8, stype: Shader.ShaderType) !Shader {
const shader = try Shader.fromFile(file, stype, std.heap.raw_c_allocator);
if (shader.compile()) |msg| {
std.log.err("[opengl] Failed to compile shader: {s}\n {s}", .{ file, msg });
}
return shader;
}
pub fn run(this: App) !void {
// == STARTUP
// Create vertex array object
...
// Create vertex buffer object
...
// Create element buffer object
...
// Vertex attributes
...
// Create shaders
const vertex_shader = try createCompiledShader("assets/shaders/shader.vert", .Vertex);
const fragment_shader = try createCompiledShader("assets/shaders/shader.frag", .Fragment);
// Create shader program
const shader_program = ShaderProgram.init(&.{
vertex_shader,
fragment_shader,
});
if (shader_program.link()) |msg| {
std.log.err("[opengl] Failed to link shader program:\n {s}", .{msg});
}
// Activate program and delete shaders
shader_program.use();
vertex_shader.delete();
fragment_shader.delete();
// == RENDER LOOP
while (!this.window.shouldClose()) {
gl.ClearColor(0.5, 0.3, 0.1, 1.0);
gl.Clear(gl.COLOR_BUFFER_BIT);
shader_program.use();
gl.BindVertexArray(vao);
gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0);
this.window.swapBuffers();
glfw.pollEvents();
}
}
// shortened Shader class
...
pub const ShaderType = enum {
Vertex,
Fragment,
};
/// The source code of the shader.
source: []const u8,
gl_object: gl.uint,
fn createOpenglShader(src: []const u8, shader_type: ShaderType) gl.uint {
const stype: gl.uint = switch (shader_type) {
.Vertex => gl.VERTEX_SHADER,
.Fragment => gl.FRAGMENT_SHADER,
};
const object: gl.uint = gl.CreateShader(stype);
gl.ShaderSource(object, 1, @ptrCast(&src.ptr), null);
return object;
}
/// Creates a `Shader` object from the source string.
pub fn fromString(src: []const u8, shader_type: ShaderType) Shader {
...
}
/// Creates a `Shader` object from the file contents of the given `path`.
/// The file path has to be relative to the folder where the executable resides.
/// If you want to use a file outside of that folder, open the file yourself and pass its' contents to `Shader.fromString`.
/// For some reason, you can't pass a `GeneralPurposeAllocator` since the program segfaults if you do.
pub fn fromFile(path: []const u8, shader_type: ShaderType, alloc: std.mem.Allocator) !Shader {
var arena = std.heap.ArenaAllocator.init(alloc);
defer arena.deinit();
const allocator = arena.allocator();
var root_dir = try std.fs.openDirAbsolute(try std.fs.selfExeDirPathAlloc(allocator), .{});
defer root_dir.close();
var file = try root_dir.openFile(path, .{});
defer file.close();
const buf = try allocator.alloc(u8, try file.getEndPos());
_ = try file.readAll(buf);
return .{
.source = buf,
.gl_object = createOpenglShader(buf, shader_type),
};
}
/// Compiles the shader. If compilation succeeded, the return value will be null.
/// Otherwise, the return value will be the error message.
pub fn compile(self: Shader) ?[256]u8 {
gl.CompileShader(self.gl_object);
var success: gl.int = undefined;
gl.GetShaderiv(self.gl_object, gl.COMPILE_STATUS, &success);
if (success == 0) {
var info_log = std.mem.zeroes([256]u8);
gl.GetShaderInfoLog(self.gl_object, 256, null, &info_log);
return info_log;
}
return null;
}
pub fn delete(self: Shader) void {
...
}
```