Merge pull request 'Configure SDL2 to use OpenGL' (#4) from opengl into main
Reviewed-on: #4
This commit is contained in:
		| @@ -31,6 +31,9 @@ pub fn build(b: *std.build.Builder) void { | ||||
|     // TOML Library | ||||
|     exe.addPackagePath("toml", "lib/zig-toml/src/toml.zig"); | ||||
|  | ||||
|     // OpenGL 3.3 Bindings | ||||
|     exe.addPackagePath("gl", "lib/gl.zig"); | ||||
|  | ||||
|     // Zig SDL Bindings: https://github.com/MasterQ32/SDL.zig | ||||
|     const sdk = Sdk.init(b); | ||||
|     sdk.link(exe, .dynamic); | ||||
|   | ||||
							
								
								
									
										5028
									
								
								lib/gl.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5028
									
								
								lib/gl.zig
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -12,9 +12,6 @@ const Allocator = std.mem.Allocator; | ||||
| const log = std.log.scoped(.PPU); | ||||
| const pollDmaOnBlank = @import("bus/dma.zig").pollDmaOnBlank; | ||||
|  | ||||
| /// This is used to generate byuu / Talurabi's Color Correction algorithm | ||||
| const COLOUR_LUT = genColourLut(); | ||||
|  | ||||
| pub const width = 240; | ||||
| pub const height = 160; | ||||
| pub const framebuf_pitch = width * @sizeOf(u32); | ||||
| @@ -392,7 +389,7 @@ pub const Ppu = struct { | ||||
|                     const maybe_btm = self.scanline.btm()[i]; | ||||
|  | ||||
|                     const bgr555 = self.getBgr555(maybe_top, maybe_btm); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], COLOUR_LUT[bgr555 & 0x7FFF]); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555)); | ||||
|                 } | ||||
|  | ||||
|                 // Reset Current Scanline Pixel Buffer and list of fetched sprites | ||||
| @@ -419,7 +416,7 @@ pub const Ppu = struct { | ||||
|                     const maybe_btm = self.scanline.btm()[i]; | ||||
|  | ||||
|                     const bgr555 = self.getBgr555(maybe_top, maybe_btm); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], COLOUR_LUT[bgr555 & 0x7FFF]); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555)); | ||||
|                 } | ||||
|  | ||||
|                 // Reset Current Scanline Pixel Buffer and list of fetched sprites | ||||
| @@ -445,7 +442,7 @@ pub const Ppu = struct { | ||||
|                     const maybe_btm = self.scanline.btm()[i]; | ||||
|  | ||||
|                     const bgr555 = self.getBgr555(maybe_top, maybe_btm); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], COLOUR_LUT[bgr555 & 0x7FFF]); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555)); | ||||
|                 } | ||||
|  | ||||
|                 // Reset Current Scanline Pixel Buffer and list of fetched sprites | ||||
| @@ -460,7 +457,7 @@ pub const Ppu = struct { | ||||
|                 var i: usize = 0; | ||||
|                 while (i < width) : (i += 1) { | ||||
|                     const bgr555 = self.vram.read(u16, vram_base + i * @sizeOf(u16)); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], COLOUR_LUT[bgr555 & 0x7FFF]); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555)); | ||||
|                 } | ||||
|             }, | ||||
|             0x4 => { | ||||
| @@ -471,7 +468,7 @@ pub const Ppu = struct { | ||||
|                 // Render Current Scanline | ||||
|                 for (self.vram.buf[vram_base .. vram_base + width]) |byte, i| { | ||||
|                     const bgr555 = self.palette.read(u16, @as(u16, byte) * @sizeOf(u16)); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], COLOUR_LUT[bgr555 & 0x7FFF]); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555)); | ||||
|                 } | ||||
|             }, | ||||
|             0x5 => { | ||||
| @@ -488,7 +485,7 @@ pub const Ppu = struct { | ||||
|                     const bgr555 = | ||||
|                         if (scanline < m5_height and i < m5_width) self.vram.read(u16, vram_base + i * @sizeOf(u16)) else self.palette.getBackdrop(); | ||||
|  | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], COLOUR_LUT[bgr555 & 0x7FFF]); | ||||
|                     std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555)); | ||||
|                 } | ||||
|             }, | ||||
|             else => std.debug.panic("[PPU] TODO: Implement BG Mode {}", .{bg_mode}), | ||||
| @@ -1063,7 +1060,7 @@ fn spriteDimensions(shape: u2, size: u2) [2]u8 { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| fn toRgba8888(bgr555: u16) u32 { | ||||
| inline fn rgba888(bgr555: u16) u32 { | ||||
|     const b = @as(u32, bgr555 >> 10 & 0x1F); | ||||
|     const g = @as(u32, bgr555 >> 5 & 0x1F); | ||||
|     const r = @as(u32, bgr555 & 0x1F); | ||||
| @@ -1071,39 +1068,6 @@ fn toRgba8888(bgr555: u16) u32 { | ||||
|     return (r << 3 | r >> 2) << 24 | (g << 3 | g >> 2) << 16 | (b << 3 | b >> 2) << 8 | 0xFF; | ||||
| } | ||||
|  | ||||
| fn genColourLut() [0x8000]u32 { | ||||
|     return comptime { | ||||
|         @setEvalBranchQuota(0x10001); | ||||
|  | ||||
|         var lut: [0x8000]u32 = undefined; | ||||
|         for (lut) |*px, i| px.* = toRgba8888(i); | ||||
|         return lut; | ||||
|     }; | ||||
| } | ||||
|  | ||||
| // FIXME: The implementation is incorrect and using it in the LUT crashes the compiler (OOM) | ||||
| /// Implementation courtesy of byuu and Talarubi at https://near.sh/articles/video/color-emulation | ||||
| fn toRgba8888Talarubi(bgr555: u16) u32 { | ||||
|     @setRuntimeSafety(false); | ||||
|  | ||||
|     const lcd_gamma: f64 = 4; | ||||
|     const out_gamma: f64 = 2.2; | ||||
|  | ||||
|     const b = @as(u32, bgr555 >> 10 & 0x1F); | ||||
|     const g = @as(u32, bgr555 >> 5 & 0x1F); | ||||
|     const r = @as(u32, bgr555 & 0x1F); | ||||
|  | ||||
|     const lb = std.math.pow(f64, @intToFloat(f64, b << 3 | b >> 2) / 31, lcd_gamma); | ||||
|     const lg = std.math.pow(f64, @intToFloat(f64, g << 3 | g >> 2) / 31, lcd_gamma); | ||||
|     const lr = std.math.pow(f64, @intToFloat(f64, r << 3 | r >> 2) / 31, lcd_gamma); | ||||
|  | ||||
|     const out_b = std.math.pow(f64, (220 * lb + 10 * lg + 50 * lr) / 255, 1 / out_gamma); | ||||
|     const out_g = std.math.pow(f64, (30 * lb + 230 * lg + 10 * lr) / 255, 1 / out_gamma); | ||||
|     const out_r = std.math.pow(f64, (0 * lb + 50 * lg + 255 * lr) / 255, 1 / out_gamma); | ||||
|  | ||||
|     return @floatToInt(u32, out_r) << 24 | @floatToInt(u32, out_g) << 16 | @floatToInt(u32, out_b) << 8 | 0xFF; | ||||
| } | ||||
|  | ||||
| fn alphaBlend(top: u16, btm: u16, bldalpha: io.BldAlpha) u16 { | ||||
|     const eva: u16 = bldalpha.eva.read(); | ||||
|     const evb: u16 = bldalpha.evb.read(); | ||||
|   | ||||
							
								
								
									
										146
									
								
								src/platform.zig
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								src/platform.zig
									
									
									
									
									
								
							| @@ -1,5 +1,6 @@ | ||||
| const std = @import("std"); | ||||
| const SDL = @import("sdl2"); | ||||
| const gl = @import("gl"); | ||||
| const emu = @import("core/emu.zig"); | ||||
| const config = @import("config.zig"); | ||||
|  | ||||
| @@ -11,21 +12,43 @@ const FpsTracker = @import("util.zig").FpsTracker; | ||||
| const span = @import("util.zig").span; | ||||
|  | ||||
| const pitch = @import("core/ppu.zig").framebuf_pitch; | ||||
| const gba_width = @import("core/ppu.zig").width; | ||||
| const gba_height = @import("core/ppu.zig").height; | ||||
|  | ||||
| const default_title: []const u8 = "ZBA"; | ||||
|  | ||||
| pub const Gui = struct { | ||||
|     const Self = @This(); | ||||
|     const SDL_GLContext = *anyopaque; // SDL.SDL_GLContext is a ?*anyopaque | ||||
|     const log = std.log.scoped(.Gui); | ||||
|  | ||||
|     // zig fmt: off | ||||
|     const vertices: [32]f32 = [_]f32{ | ||||
|         // Positions        // Colours      // Texture Coords | ||||
|          1.0, -1.0, 0.0,    1.0, 0.0, 0.0,  1.0, 1.0, // Top Right | ||||
|          1.0,  1.0, 0.0,    0.0, 1.0, 0.0,  1.0, 0.0, // Bottom Right | ||||
|         -1.0,  1.0, 0.0,    0.0, 0.0, 1.0,  0.0, 0.0, // Bottom Left | ||||
|         -1.0, -1.0, 0.0,    1.0, 1.0, 0.0,  0.0, 1.0, // Top Left | ||||
|     }; | ||||
|  | ||||
|     const indices: [6]u32 = [_]u32{ | ||||
|         0, 1, 3, // First Triangle | ||||
|         1, 2, 3, // Second Triangle | ||||
|     }; | ||||
|     // zig fmt: on | ||||
|  | ||||
|     window: *SDL.SDL_Window, | ||||
|     ctx: SDL_GLContext, | ||||
|     title: []const u8, | ||||
|     renderer: *SDL.SDL_Renderer, | ||||
|     texture: *SDL.SDL_Texture, | ||||
|     audio: Audio, | ||||
|  | ||||
|     program_id: gl.GLuint, | ||||
|  | ||||
|     pub fn init(title: *const [12]u8, apu: *Apu, width: i32, height: i32) Self { | ||||
|         const ret = SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO | SDL.SDL_INIT_GAMECONTROLLER); | ||||
|         if (ret < 0) panic(); | ||||
|         if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO) < 0) panic(); | ||||
|         if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_PROFILE_MASK, SDL.SDL_GL_CONTEXT_PROFILE_CORE) < 0) panic(); | ||||
|         if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0) panic(); | ||||
|         if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0) panic(); | ||||
|  | ||||
|         const win_scale = @intCast(c_int, config.config().host.win_scale); | ||||
|  | ||||
| @@ -35,29 +58,98 @@ pub const Gui = struct { | ||||
|             SDL.SDL_WINDOWPOS_CENTERED, | ||||
|             @as(c_int, width * win_scale), | ||||
|             @as(c_int, height * win_scale), | ||||
|             SDL.SDL_WINDOW_SHOWN, | ||||
|             SDL.SDL_WINDOW_OPENGL | SDL.SDL_WINDOW_SHOWN, | ||||
|         ) orelse panic(); | ||||
|  | ||||
|         const renderer_flags = SDL.SDL_RENDERER_ACCELERATED | if (config.config().host.vsync) SDL.SDL_RENDERER_PRESENTVSYNC else 0; | ||||
|         const renderer = SDL.SDL_CreateRenderer(window, -1, @bitCast(u32, renderer_flags)) orelse panic(); | ||||
|         const ctx = SDL.SDL_GL_CreateContext(window) orelse panic(); | ||||
|         if (SDL.SDL_GL_MakeCurrent(window, ctx) < 0) panic(); | ||||
|  | ||||
|         const texture = SDL.SDL_CreateTexture( | ||||
|             renderer, | ||||
|             SDL.SDL_PIXELFORMAT_RGBA8888, | ||||
|             SDL.SDL_TEXTUREACCESS_STREAMING, | ||||
|             @as(c_int, width), | ||||
|             @as(c_int, height), | ||||
|         ) orelse panic(); | ||||
|         gl.load(ctx, Self.glGetProcAddress) catch @panic("gl.load failed"); | ||||
|         if (SDL.SDL_GL_SetSwapInterval(@boolToInt(config.config().host.vsync)) < 0) panic(); | ||||
|  | ||||
|         const program_id = compileShaders(); | ||||
|  | ||||
|         return Self{ | ||||
|             .window = window, | ||||
|             .title = span(title), | ||||
|             .renderer = renderer, | ||||
|             .texture = texture, | ||||
|             .ctx = ctx, | ||||
|             .program_id = program_id, | ||||
|             .audio = Audio.init(apu), | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     fn compileShaders() gl.GLuint { | ||||
|         // TODO: Panic on Shader Compiler Failure + Error Message | ||||
|         const vert_shader = @embedFile("shader/pixelbuf.vert"); | ||||
|         const frag_shader = @embedFile("shader/pixelbuf.frag"); | ||||
|  | ||||
|         const vs = gl.createShader(gl.VERTEX_SHADER); | ||||
|         defer gl.deleteShader(vs); | ||||
|  | ||||
|         gl.shaderSource(vs, 1, &[_][*c]const u8{vert_shader}, 0); | ||||
|         gl.compileShader(vs); | ||||
|  | ||||
|         const fs = gl.createShader(gl.FRAGMENT_SHADER); | ||||
|         defer gl.deleteShader(fs); | ||||
|  | ||||
|         gl.shaderSource(fs, 1, &[_][*c]const u8{frag_shader}, 0); | ||||
|         gl.compileShader(fs); | ||||
|  | ||||
|         const program = gl.createProgram(); | ||||
|         gl.attachShader(program, vs); | ||||
|         gl.attachShader(program, fs); | ||||
|         gl.linkProgram(program); | ||||
|  | ||||
|         return program; | ||||
|     } | ||||
|  | ||||
|     // Returns the VAO ID since it's used in run() | ||||
|     fn generateBuffers() [3]c_uint { | ||||
|         var vao_id: c_uint = undefined; | ||||
|         var vbo_id: c_uint = undefined; | ||||
|         var ebo_id: c_uint = undefined; | ||||
|         gl.genVertexArrays(1, &vao_id); | ||||
|         gl.genBuffers(1, &vbo_id); | ||||
|         gl.genBuffers(1, &ebo_id); | ||||
|  | ||||
|         gl.bindVertexArray(vao_id); | ||||
|  | ||||
|         gl.bindBuffer(gl.ARRAY_BUFFER, vbo_id); | ||||
|         gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(@TypeOf(vertices)), &vertices, gl.STATIC_DRAW); | ||||
|  | ||||
|         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo_id); | ||||
|         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, @sizeOf(@TypeOf(indices)), &indices, gl.STATIC_DRAW); | ||||
|  | ||||
|         // Position | ||||
|         gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), @intToPtr(?*anyopaque, 0)); // lmao | ||||
|         gl.enableVertexAttribArray(0); | ||||
|         // Colour | ||||
|         gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), @intToPtr(?*anyopaque, (3 * @sizeOf(f32)))); | ||||
|         gl.enableVertexAttribArray(1); | ||||
|         // Texture Coord | ||||
|         gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), @intToPtr(?*anyopaque, (6 * @sizeOf(f32)))); | ||||
|         gl.enableVertexAttribArray(2); | ||||
|  | ||||
|         return .{ vao_id, vbo_id, ebo_id }; | ||||
|     } | ||||
|  | ||||
|     fn generateTexture(buf: []const u8) c_uint { | ||||
|         var tex_id: c_uint = undefined; | ||||
|         gl.genTextures(1, &tex_id); | ||||
|         gl.bindTexture(gl.TEXTURE_2D, tex_id); | ||||
|  | ||||
|         // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | ||||
|         // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | ||||
|  | ||||
|         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); | ||||
|         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); | ||||
|  | ||||
|         gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gba_width, gba_height, 0, gl.RGBA, gl.UNSIGNED_INT_8_8_8_8, buf.ptr); | ||||
|         // gl.generateMipmap(gl.TEXTURE_2D); // TODO: Remove? | ||||
|  | ||||
|         return tex_id; | ||||
|     } | ||||
|  | ||||
|     pub fn run(self: *Self, cpu: *Arm7tdmi, scheduler: *Scheduler) !void { | ||||
|         var quit = std.atomic.Atomic(bool).init(false); | ||||
|         var tracker = FpsTracker.init(); | ||||
| @@ -67,6 +159,9 @@ pub const Gui = struct { | ||||
|  | ||||
|         var title_buf: [0x100]u8 = [_]u8{0} ** 0x100; | ||||
|  | ||||
|         const vao_id = Self.generateBuffers()[0]; | ||||
|         _ = Self.generateTexture(cpu.bus.ppu.framebuf.get(.Renderer)); | ||||
|  | ||||
|         emu_loop: while (true) { | ||||
|             var event: SDL.SDL_Event = undefined; | ||||
|             while (SDL.SDL_PollEvent(&event) != 0) { | ||||
| @@ -125,9 +220,12 @@ pub const Gui = struct { | ||||
|  | ||||
|             // Emulator has an internal Double Buffer | ||||
|             const framebuf = cpu.bus.ppu.framebuf.get(.Renderer); | ||||
|             _ = SDL.SDL_UpdateTexture(self.texture, null, framebuf.ptr, pitch); | ||||
|             _ = SDL.SDL_RenderCopy(self.renderer, self.texture, null, null); | ||||
|             SDL.SDL_RenderPresent(self.renderer); | ||||
|             gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gba_width, gba_height, gl.RGBA, gl.UNSIGNED_INT_8_8_8_8, framebuf.ptr); | ||||
|  | ||||
|             gl.useProgram(self.program_id); | ||||
|             gl.bindVertexArray(vao_id); | ||||
|             gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, null); | ||||
|             SDL.SDL_GL_SwapWindow(self.window); | ||||
|  | ||||
|             const dyn_title = std.fmt.bufPrint(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, tracker.value() }) catch unreachable; | ||||
|             SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr); | ||||
| @@ -138,12 +236,18 @@ pub const Gui = struct { | ||||
|  | ||||
|     pub fn deinit(self: *Self) void { | ||||
|         self.audio.deinit(); | ||||
|         SDL.SDL_DestroyTexture(self.texture); | ||||
|         SDL.SDL_DestroyRenderer(self.renderer); | ||||
|         // TODO: Buffer deletions | ||||
|         gl.deleteProgram(self.program_id); | ||||
|         SDL.SDL_GL_DeleteContext(self.ctx); | ||||
|         SDL.SDL_DestroyWindow(self.window); | ||||
|         SDL.SDL_Quit(); | ||||
|         self.* = undefined; | ||||
|     } | ||||
|  | ||||
|     fn glGetProcAddress(ctx: SDL.SDL_GLContext, proc: [:0]const u8) ?*anyopaque { | ||||
|         _ = ctx; | ||||
|         return SDL.SDL_GL_GetProcAddress(proc.ptr); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const Audio = struct { | ||||
|   | ||||
							
								
								
									
										25
									
								
								src/shader/pixelbuf.frag
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/shader/pixelbuf.frag
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| #version 330 core | ||||
| out vec4 frag_color; | ||||
|  | ||||
| in vec3 color; | ||||
| in vec2 uv; | ||||
|  | ||||
| uniform sampler2D screen; | ||||
|  | ||||
| void main() { | ||||
| 	// https://near.sh/video/color-emulation | ||||
| 	// Thanks to Talarubi + Near for the Colour Correction | ||||
| 	// Thanks to fleur + mattrb for the Shader Impl | ||||
|  | ||||
| 	vec4 color = texture(screen, uv); | ||||
| 	color.rgb = pow(color.rgb, vec3(4.0)); // LCD Gamma | ||||
|    | ||||
| 	frag_color = vec4( | ||||
| 		pow(vec3( | ||||
| 		  	  0 * color.b +  50 * color.g + 255 * color.r, | ||||
| 	     	 30 * color.b + 230 * color.g +  10 * color.r, | ||||
| 			220 * color.b +  10 * color.g +  50 * color.r | ||||
| 		) / 255, vec3(1.0 / 2.2)), // Out Gamma | ||||
| 	1.0);  | ||||
| } | ||||
|  | ||||
							
								
								
									
										13
									
								
								src/shader/pixelbuf.vert
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/shader/pixelbuf.vert
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| #version 330 core | ||||
| layout (location = 0) in vec3 pos; | ||||
| layout (location = 1) in vec3 in_color; | ||||
| layout (location = 2) in vec2 in_uv; | ||||
|  | ||||
| out vec3 color; | ||||
| out vec2 uv; | ||||
|  | ||||
| void main() { | ||||
| 	color = in_color; | ||||
| 	uv = in_uv; | ||||
| 	gl_Position = vec4(pos, 1.0); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user