opengl/learn_opengl/ 1_getting_started/ 7_camera
Licence This example has its own licence, rather than Odin's: opengl/learn_opengl/LICENSE
LearnOpenGL.com © 2025 by Joey de Vries is licensed under CC BY-NC 4.0. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc/4.0/
https://learnopengl.com
https://x.com/JoeyDeVriez
Code
main.odin ¶297 linesSource
1package main
2
3import "core:math/linalg/glsl"
4import gl "vendor:OpenGL"
5import "vendor:glfw"
6import "core:fmt"
7import "core:os"
8import stbi "vendor:stb/image"
9import "core:math"
10
11framebuffer_size_callback :: proc "c" (window: glfw.WindowHandle, width: i32, height: i32) {
12 gl.Viewport(0, 0, width, height)
13}
14
15processInput :: proc "c" (window: glfw.WindowHandle) {
16 if glfw.GetKey(window, glfw.KEY_ESCAPE) == glfw.PRESS {
17 glfw.SetWindowShouldClose(window, true)
18 }
19
20 cameraSpeed := f32(deltaTime * 2.5)
21 if glfw.GetKey(window, glfw.KEY_W) == glfw.PRESS {
22 cameraPos += cameraSpeed * cameraFront
23 }
24 if glfw.GetKey(window, glfw.KEY_S) == glfw.PRESS {
25 cameraPos -= cameraSpeed * cameraFront
26 }
27 if glfw.GetKey(window, glfw.KEY_A) == glfw.PRESS {
28 cameraPos -= glsl.normalize(glsl.cross(cameraFront, cameraUp)) * cameraSpeed
29 }
30 if glfw.GetKey(window, glfw.KEY_D) == glfw.PRESS {
31 cameraPos += glsl.normalize(glsl.cross(cameraFront, cameraUp)) * cameraSpeed
32 }
33}
34
35mouse_callback :: proc "c" (window: glfw.WindowHandle, xposIn: f64, yposIn: f64) {
36 xpos := f32(xposIn)
37 ypos := f32(yposIn)
38
39 if firstMouse {
40 lastX = xpos
41 lastY = ypos
42 firstMouse = false
43 }
44
45 xoffset := xpos - lastX
46 yoffset := lastY - ypos
47 lastX = xpos
48 lastY = ypos
49
50 sensitivity: f32 = 0.1
51 xoffset *= sensitivity
52 yoffset *= sensitivity
53
54 yaw += xoffset
55 pitch += yoffset
56
57 if pitch > 89 {
58 pitch = 89
59 }
60 if pitch < -89 {
61 pitch = -89
62 }
63
64 front := glsl.vec3{
65 math.cos(glsl.radians(yaw)) * math.cos(glsl.radians(pitch)),
66 math.sin(glsl.radians(pitch)),
67 math.sin(glsl.radians(yaw)) * glsl.cos(glsl.radians(pitch)),
68 }
69 cameraFront = glsl.normalize(front)
70}
71
72scroll_callback := proc "c" (window: glfw.WindowHandle, xoffset: f64, yoffset: f64) {
73 fov -= f32(yoffset)
74 if fov < 1 {
75 fov = 1
76 }
77 if fov > 45 {
78 fov = 45
79 }
80}
81
82SCR_WIDTH :: 800
83SCR_HEIGHT :: 600
84
85cameraPos := glsl.vec3{0, 0, 3}
86cameraFront := glsl.vec3{0, 0, -1}
87cameraUp := glsl.vec3{0, 1, 0}
88
89firstMouse := true
90yaw: f32 = -90
91pitch: f32 = 0
92lastX: f32 = 800 / 2
93lastY: f32 = 600 / 2
94fov: f32 = 45
95
96deltaTime: f32 = 0
97lastFrame: f32 = 0
98
99main :: proc() {
100 glfw.Init()
101 glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
102 glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
103 glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
104
105 window := glfw.CreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nil, nil)
106 if window == nil {
107 fmt.println("Failed to create GLFW window")
108 glfw.Terminate()
109 os.exit(-1)
110 }
111 glfw.MakeContextCurrent(window)
112
113 gl.load_up_to(3, 3, glfw.gl_set_proc_address)
114
115 gl.Viewport(0, 0, SCR_WIDTH, SCR_HEIGHT)
116
117 glfw.SetFramebufferSizeCallback(window, framebuffer_size_callback)
118 glfw.SetCursorPosCallback(window, mouse_callback)
119 glfw.SetScrollCallback(window, scroll_callback)
120
121 glfw.SetInputMode(window, glfw.CURSOR, glfw.CURSOR_DISABLED)
122
123 vertices := [?]f32 {
124 -0.5, -0.5, -0.5, 0.0, 0.0,
125 0.5, -0.5, -0.5, 1.0, 0.0,
126 0.5, 0.5, -0.5, 1.0, 1.0,
127 0.5, 0.5, -0.5, 1.0, 1.0,
128 -0.5, 0.5, -0.5, 0.0, 1.0,
129 -0.5, -0.5, -0.5, 0.0, 0.0,
130
131 -0.5, -0.5, 0.5, 0.0, 0.0,
132 0.5, -0.5, 0.5, 1.0, 0.0,
133 0.5, 0.5, 0.5, 1.0, 1.0,
134 0.5, 0.5, 0.5, 1.0, 1.0,
135 -0.5, 0.5, 0.5, 0.0, 1.0,
136 -0.5, -0.5, 0.5, 0.0, 0.0,
137
138 -0.5, 0.5, 0.5, 1.0, 0.0,
139 -0.5, 0.5, -0.5, 1.0, 1.0,
140 -0.5, -0.5, -0.5, 0.0, 1.0,
141 -0.5, -0.5, -0.5, 0.0, 1.0,
142 -0.5, -0.5, 0.5, 0.0, 0.0,
143 -0.5, 0.5, 0.5, 1.0, 0.0,
144
145 0.5, 0.5, 0.5, 1.0, 0.0,
146 0.5, 0.5, -0.5, 1.0, 1.0,
147 0.5, -0.5, -0.5, 0.0, 1.0,
148 0.5, -0.5, -0.5, 0.0, 1.0,
149 0.5, -0.5, 0.5, 0.0, 0.0,
150 0.5, 0.5, 0.5, 1.0, 0.0,
151
152 -0.5, -0.5, -0.5, 0.0, 1.0,
153 0.5, -0.5, -0.5, 1.0, 1.0,
154 0.5, -0.5, 0.5, 1.0, 0.0,
155 0.5, -0.5, 0.5, 1.0, 0.0,
156 -0.5, -0.5, 0.5, 0.0, 0.0,
157 -0.5, -0.5, -0.5, 0.0, 1.0,
158
159 -0.5, 0.5, -0.5, 0.0, 1.0,
160 0.5, 0.5, -0.5, 1.0, 1.0,
161 0.5, 0.5, 0.5, 1.0, 0.0,
162 0.5, 0.5, 0.5, 1.0, 0.0,
163 -0.5, 0.5, 0.5, 0.0, 0.0,
164 -0.5, 0.5, -0.5, 0.0, 1.0,
165 }
166
167 cubePositions := [?]glsl.vec3{
168 {0.0, 0.0, 0.0},
169 {2.0, 5.0, -15.0},
170 {-1.5, -2.2, -2.5},
171 {-3.8, -2.0, -12.3},
172 {2.4, -0.4, -3.5},
173 {-1.7, 3.0, -7.5},
174 {1.3, -2.0, -2.5},
175 {1.5, 2.0, -2.5},
176 {1.5, 0.2, -1.5},
177 {-1.3, 1.0, -1.5},
178 }
179
180 gl.Enable(gl.DEPTH_TEST)
181
182 // The shader loading they create can be replaced with just this
183 shaderProgram, loaded_ok := gl.load_shaders_file("./res/vertex.vs", "./res/fragment.fs")
184 if !loaded_ok {
185 os.exit(-1)
186 }
187
188 VBO, VAO: u32
189 gl.GenVertexArrays(1, &VAO)
190 gl.GenBuffers(1, &VBO)
191
192 gl.BindVertexArray(VAO)
193
194 gl.BindBuffer(gl.ARRAY_BUFFER, VBO)
195 gl.BufferData(gl.ARRAY_BUFFER, size_of(vertices), raw_data(&vertices), gl.STATIC_DRAW)
196
197 gl.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 5 * size_of(f32), 0)
198 gl.EnableVertexAttribArray(0)
199
200 gl.VertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 5 * size_of(f32), 3 * size_of(f32))
201 gl.EnableVertexAttribArray(1)
202
203 texture1, texture2: u32
204 gl.GenTextures(1, &texture1)
205 gl.BindTexture(gl.TEXTURE_2D, texture1)
206
207 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
208 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
209
210 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
211 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
212
213 width, height, nrChannels: i32
214 stbi.set_flip_vertically_on_load(1)
215
216 data := stbi.load("../res/container.jpg", &width, &height, &nrChannels, 0)
217 if data == nil {
218 fmt.println("Failed to load texture")
219 os.exit(-1)
220 }
221
222 gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, data)
223 gl.GenerateMipmap(gl.TEXTURE_2D)
224
225 stbi.image_free(data)
226
227 gl.GenTextures(1, &texture2)
228 gl.BindTexture(gl.TEXTURE_2D, texture2)
229
230 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
231 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
232
233 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
234 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
235
236 data = stbi.load("../res/awesomeface.png", &width, &height, &nrChannels, 0)
237 if data == nil {
238 fmt.println("Failed to load texture")
239 os.exit(-1)
240 }
241
242 gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data)
243 gl.GenerateMipmap(gl.TEXTURE_2D)
244
245 stbi.image_free(data)
246
247 gl.UseProgram(shaderProgram)
248 shader_set_int(shaderProgram, "texture1", 0)
249 shader_set_int(shaderProgram, "texture2", 1)
250
251 for !glfw.WindowShouldClose(window) {
252 currentFrame := f32(glfw.GetTime())
253 deltaTime = currentFrame - lastFrame
254 lastFrame = currentFrame
255
256 processInput(window)
257
258 gl.ClearColor(0.2, 0.3, 0.3, 1.0)
259 gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
260
261 gl.ActiveTexture(gl.TEXTURE0)
262 gl.BindTexture(gl.TEXTURE_2D, texture1)
263 gl.ActiveTexture(gl.TEXTURE1)
264 gl.BindTexture(gl.TEXTURE_2D, texture2)
265
266 gl.UseProgram(shaderProgram)
267
268 view: glsl.mat4 = 1
269 view *= glsl.mat4LookAt(cameraPos, cameraPos + cameraFront, cameraUp)
270
271 projection: glsl.mat4 = 1
272 projection *= glsl.mat4Perspective(glsl.radians_f32(45), f32(f32(SCR_WIDTH) / f32(SCR_HEIGHT)), 0.1, 100)
273
274 shader_set_mat4(shaderProgram, "projection", projection)
275 shader_set_mat4(shaderProgram, "view", view)
276
277 gl.BindVertexArray(VAO)
278 for position, i in cubePositions {
279 model: glsl.mat4 = 1
280 model *= glsl.mat4Translate(position)
281 angle := 20.0 * f32(i)
282 model *= glsl.mat4Rotate({1.0, 0.3, 0.5}, glsl.radians(angle))
283 shader_set_mat4(shaderProgram, "model", model)
284
285 gl.DrawArrays(gl.TRIANGLES, 0, 36)
286 }
287
288 glfw.SwapBuffers(window)
289 glfw.PollEvents()
290 }
291
292 gl.DeleteVertexArrays(1, &VAO)
293 gl.DeleteBuffers(1, &VBO)
294 gl.DeleteProgram(shaderProgram)
295
296 glfw.Terminate()
297}
shader.odin ¶73 linesSource
1package main
2
3import gl "vendor:OpenGL"
4import "core:math/linalg/glsl"
5
6shader_set_bool :: proc(id: u32, name: cstring, value: bool) {
7 gl.Uniform1i(gl.GetUniformLocation(id, name), i32(value))
8}
9
10shader_set_int :: proc(id: u32, name: cstring, value: i32) {
11 gl.Uniform1i(gl.GetUniformLocation(id, name), value)
12}
13
14shader_set_float :: proc(id: u32, name: cstring, value: f32) {
15 gl.Uniform1f(gl.GetUniformLocation(id, name), value)
16}
17
18shader_set_vec2_v :: proc(id: u32, name: cstring, value: glsl.vec2) {
19 lc := value
20 gl.Uniform2fv(gl.GetUniformLocation(id, name), 1, raw_data(&lc))
21}
22
23shader_set_vec2_f :: proc(id: u32, name: cstring, x, y: f32) {
24 gl.Uniform2f(gl.GetUniformLocation(id, name), x, y)
25}
26
27shader_set_vec2 :: proc {
28 shader_set_vec2_v,
29 shader_set_vec2_f,
30}
31
32shader_set_vec3_v :: proc(id: u32, name: cstring, value: glsl.vec3) {
33 lc := value
34 gl.Uniform3fv(gl.GetUniformLocation(id, name), 1, raw_data(&lc))
35}
36
37shader_set_vec3_f :: proc(id: u32, name: cstring, x, y, z: f32) {
38 gl.Uniform3f(gl.GetUniformLocation(id, name), x, y, z)
39}
40
41shader_set_vec3 :: proc {
42 shader_set_vec3_v,
43 shader_set_vec3_f,
44}
45
46shader_set_vec4_v :: proc(id: u32, name: cstring, value: glsl.vec4) {
47 lc := value
48 gl.Uniform4fv(gl.GetUniformLocation(id, name), 1, raw_data(&lc))
49}
50
51shader_set_vec4_f :: proc(id: u32, name: cstring, x, y, z, w: f32) {
52 gl.Uniform4f(gl.GetUniformLocation(id, name), x, y, z, w)
53}
54
55shader_set_vec4 :: proc {
56 shader_set_vec4_v,
57 shader_set_vec4_f,
58}
59
60shader_set_mat2 :: proc(id: u32, name: cstring, mat: glsl.mat2) {
61 lc := mat
62 gl.UniformMatrix2fv(gl.GetUniformLocation(id, name), 1, gl.FALSE, raw_data(&lc))
63}
64
65shader_set_mat3 :: proc(id: u32, name: cstring, mat: glsl.mat3) {
66 lc := mat
67 gl.UniformMatrix3fv(gl.GetUniformLocation(id, name), 1, gl.FALSE, raw_data(&lc))
68}
69
70shader_set_mat4 :: proc(id: u32, name: cstring, mat: glsl.mat4) {
71 lc := mat
72 gl.UniformMatrix4fv(gl.GetUniformLocation(id, name), 1, gl.FALSE, raw_data(&lc))
73}
Shaders
res/fragment.fs ¶14 linesSource
#version 330 core
out vec4 FragColor;
in vec2 TexCoord;
// texture samplers
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
// linearly interpolate between both textures (80% container, 20% awesomeface)
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}
res/vertex.vs ¶15 linesSource
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0f);
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}
Declarations Used 107
- core:fmt println
- core:math cos · sin
- core:math/linalg/glsl cos · cross · mat2 · mat3 · mat4 · mat4LookAt · mat4Perspective · mat4Rotate · mat4Translate · normalize · radians · radians_f32 · vec2 · vec3 · vec4
- core:os exit
- vendor:OpenGL ActiveTexture · ARRAY_BUFFER · BindBuffer · BindTexture · BindVertexArray · BufferData · Clear · ClearColor · COLOR_BUFFER_BIT · DeleteBuffers · DeleteProgram · DeleteVertexArrays · DEPTH_BUFFER_BIT · DEPTH_TEST · DrawArrays · Enable · EnableVertexAttribArray · FALSE · FLOAT · GenBuffers · GenerateMipmap · GenTextures · GenVertexArrays · GetUniformLocation · LINEAR · load_shaders_file · load_up_to · REPEAT · RGB · RGBA · STATIC_DRAW · TexImage2D · TexParameteri · TEXTURE0 · TEXTURE1 · TEXTURE_2D · TEXTURE_MAG_FILTER · TEXTURE_MIN_FILTER · TEXTURE_WRAP_S · TEXTURE_WRAP_T · TRIANGLES · Uniform1f · Uniform1i · Uniform2f · Uniform2fv · Uniform3f · Uniform3fv · Uniform4f · Uniform4fv · UniformMatrix2fv · UniformMatrix3fv · UniformMatrix4fv · UNSIGNED_BYTE · UseProgram · VertexAttribPointer · Viewport
- vendor:glfw CONTEXT_VERSION_MAJOR · CONTEXT_VERSION_MINOR · CreateWindow · CURSOR · CURSOR_DISABLED · GetKey · GetTime · gl_set_proc_address · Init · KEY_A · KEY_D · KEY_ESCAPE · KEY_S · KEY_W · MakeContextCurrent · OPENGL_CORE_PROFILE · OPENGL_PROFILE · PollEvents · PRESS · SetCursorPosCallback · SetFramebufferSizeCallback · SetInputMode · SetScrollCallback · SetWindowShouldClose · SwapBuffers · Terminate · WindowHandle · WindowHint · WindowShouldClose
- vendor:stb/image image_free · load · set_flip_vertically_on_load