シャドーマッピング

守矢諏訪子 <suwako@076.moe>、2026-06-28 00:29
コミット 8cb968dd56e61c8c41bd17ded3a6b058abae5199 ・親 ab170ac ・ファイルを見る

8個のファイル、+582 -9

@@ -40,13 +40,19 @@
4040 #include <util/structs.hh>
4141
4242 namespace gui {
43+ enum class ShaderType {
44+ Simple2D,
45+ Simple3D,
46+ Scene,
47+ };
48+
4349 class NewShader {
4450 public:
4551 NewShader(Info *info);
4652 ~NewShader();
4753
4854 void DrawPopup();
49- db::ShaderData Make(bool is3D = false);
55+ db::ShaderData Make(ShaderType type = ShaderType::Simple2D);
5056
5157 private:
5258 string getDefaultVert2D();
@@ -56,6 +62,10 @@
5662 string getDefaultVert3D();
5763 string getDefaultFrag3D();
5864 string getDefaultLua3D();
65+
66+ string getDefaultVertScene();
67+ string getDefaultFragScene();
68+ string getDefaultLuaScene();
5969
6070 private:
6171 Info *m_Info;
@@ -38,6 +38,8 @@
3838
3939 #include <glad/glad.h>
4040 #include <util/types.hh>
41+#include <util/vector.hh>
42+#include <util/matrix.hh>
4143
4244 struct Shader {
4345 GLuint id;
@@ -59,6 +61,9 @@
5961 void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
6062 void Clear(GLbitfield mask = GL_COLOR_BUFFER_BIT);
6163 GLint GetUniformLocation(string var);
64+ void SetUniformVec3(const string &name, Vector3 v);
65+ void SetUniformMat4(const string &name, Matrix4 v);
66+ void SetUniformInt(const string &name, i32 v);
6267 };
6368
6469 struct VertexLayout {
@@ -209,4 +209,20 @@
209209 return out;
210210 }
211211
212+inline Matrix4 mat4Ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) {
213+ Matrix4 m = {};
214+
215+ m.m[0] = 2.0f / (right - left);
216+ m.m[5] = 2.0f / (top - bottom);
217+ m.m[10] = -2.0f / (far - near);
218+
219+ m.m[12] = -(right + left) / (right - left);
220+ m.m[13] = -(top + bottom) / (top - bottom);
221+ m.m[14] = -(far + near) / (far - near);
222+
223+ m.m[15] = 1.0f;
224+
225+ return m;
226+}
227+
212228 #endif // MATRIX_HH
変更 main.cc
@@ -87,8 +87,69 @@
8787 return -1;
8888 }
8989
90+ // シャドーマッピング
91+#pragma region shadowmap
92+ constexpr u32 SHADOW_WIDTH = 1024;
93+ constexpr u32 SHADOW_HEIGHT = 1024;
94+
95+ GLuint shadowFBO;
96+ glGenFramebuffers(1, &shadowFBO);
97+
98+ GLuint shadowMap;
99+ glGenTextures(1, &shadowMap);
100+ glBindTexture(GL_TEXTURE_2D, shadowMap);
101+
102+ glTexImage2D(
103+ GL_TEXTURE_2D,
104+ 0,
105+ GL_DEPTH_COMPONENT,
106+ SHADOW_WIDTH,
107+ SHADOW_HEIGHT,
108+ 0,
109+ GL_DEPTH_COMPONENT,
110+ GL_FLOAT,
111+ nullptr
112+ );
113+
114+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
115+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
116+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
117+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
118+
119+ float borderColor[] = { 1.f, 1.f, 1.f, 1.f };
120+ glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
121+
122+ glBindFramebuffer(GL_FRAMEBUFFER, shadowFBO);
123+ glFramebufferTexture2D(
124+ GL_FRAMEBUFFER,
125+ GL_DEPTH_ATTACHMENT,
126+ GL_TEXTURE_2D,
127+ shadowMap,
128+ 0
129+ );
130+
131+ glDrawBuffer(GL_NONE);
132+ glReadBuffer(GL_NONE);
133+
134+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
135+
136+ string depthVertSource = R"(#version 460 core
137+layout(location = 0) in vec3 aPosition;
138+
139+uniform mat4 uLightSpace;
140+uniform mat4 uModel;
141+
142+void main() {
143+ gl_Position = uLightSpace * uModel * vec4(aPosition, 1.0);
144+})";
145+
146+ string depthFragSource = R"(#version 460 core
147+void main() { })";
148+#pragma endregion
149+
90150 // 3Dを有効
91151 glEnable(GL_DEPTH_TEST);
152+ glDepthFunc(GL_LESS);
92153 //glEnable(GL_CULL_FACE);
93154
94155 // ImGui設定
@@ -182,6 +243,12 @@
182243 gui::About about;
183244 gui::Settings settings;
184245
246+#pragma region shadowmap
247+ Shader depthVertex(depthVertSource, GL_VERTEX_SHADER);
248+ Shader depthFragment(depthFragSource, GL_FRAGMENT_SHADER);
249+ Program depthProgram(depthVertex, depthFragment);
250+#pragma endregion
251+
185252 // メインレンダリングループ
186253 while (!window.ShouldClose() && info.isRunning) {
187254 #pragma region Lambda
@@ -336,7 +403,7 @@
336403 if (ImGui::BeginPopupModal(info.i18n->GetWord("filenewcreateshader").c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
337404 if (ImGui::Button(info.i18n->GetWord("filenew2dshader").c_str(), ImVec2(200, 0))) {
338405 gui::NewShader newShader(&info);
339- auto shdr = newShader.Make(false);
406+ auto shdr = newShader.Make(gui::ShaderType::Simple2D);
340407 info.shaderId = shdr.id;
341408 info.shaderName = shdr.name;
342409 info.VERT = shdr.vertexShader;
@@ -368,7 +435,7 @@
368435
369436 if (ImGui::Button(info.i18n->GetWord("filenew3dshader").c_str(), ImVec2(200, 0))) {
370437 gui::NewShader newShader(&info);
371- auto shdr = newShader.Make(true);
438+ auto shdr = newShader.Make(gui::ShaderType::Simple3D);
372439 info.shaderId = shdr.id;
373440 info.shaderName = shdr.name;
374441 info.VERT = shdr.vertexShader;
@@ -398,6 +465,38 @@
398465 ImGui::CloseCurrentPopup();
399466 }
400467
468+ if (ImGui::Button(info.i18n->GetWord("filenewsceneshader").c_str(), ImVec2(200, 0))) {
469+ gui::NewShader newShader(&info);
470+ auto shdr = newShader.Make(gui::ShaderType::Scene);
471+ info.shaderId = shdr.id;
472+ info.shaderName = shdr.name;
473+ info.VERT = shdr.vertexShader;
474+ info.FRAG = shdr.fragmentShader;
475+ info.LUA = shdr.luaCode;
476+
477+ vertEditor.SetCode(info.VERT.code);
478+ string nTit = info.shaderName + "(" + info.i18n->GetWord("editorvertshader") + ")";
479+ vertEditor.SetTitle(nTit + "###VertexEditor");
480+
481+ fragEditor.SetCode(info.FRAG.code);
482+ nTit = info.shaderName + "(" + info.i18n->GetWord("editorfragshader") + ")";
483+ fragEditor.SetTitle(nTit + "###FragmentEditor");
484+
485+ luaEditor.SetCode(info.LUA.code);
486+ nTit = info.shaderName + "(" + info.i18n->GetWord("editorlua") + ")";
487+ luaEditor.SetTitle(nTit + "###LuaEditor");
488+
489+ info.pendingCompile = true;
490+
491+ gui::LogEntry entry;
492+ entry.type = gui::LogType::Info;
493+ entry.text = info.i18n->GetWord("consoleloginfocreateok");
494+ info.cmd->Add(entry);
495+ std::cout << entry.text << std::endl;
496+
497+ ImGui::CloseCurrentPopup();
498+ }
499+
401500 ImGui::Separator();
402501
403502 if (ImGui::Button(info.i18n->GetWord("cancel").c_str())) {
@@ -508,6 +607,74 @@
508607
509608 ImGui::Render();
510609
610+#pragma region shadowmap
611+ Matrix4 lightProjection = mat4Ortho(
612+ -20.f, 20.f,
613+ -20.f, 20.f,
614+ 0.1f, 100.f
615+ );
616+
617+ Matrix4 lightView = mat4LookAt(
618+ { 10.f, 10.f, 10.f }, // 光の位置
619+ { 0.f, 0.f, 0.f }, // ターゲット
620+ { 0.f, 1.f, 0.f } // 上
621+ );
622+
623+ Matrix4 lightSpace = mat4Mul(lightProjection, lightView);
624+
625+ glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
626+ glBindFramebuffer(GL_FRAMEBUFFER, shadowFBO);
627+ glClear(GL_DEPTH_BUFFER_BIT);
628+
629+ depthProgram.Use();
630+ depthProgram.SetUniformMat4("uLightSpace", lightSpace);
631+
632+ for (auto &obj : objects) {
633+ if (obj.mesh.vertices.empty()) continue;
634+ if (obj.mesh.indices.empty()) continue;
635+
636+ Matrix4 T = mat4Translate(
637+ obj.position.pos.x,
638+ obj.position.pos.y,
639+ obj.position.pos.z
640+ );
641+
642+ Matrix4 R = mat4Rotate(
643+ obj.rotation.pos.x,
644+ obj.rotation.pos.y,
645+ obj.rotation.pos.z
646+ );
647+
648+ Matrix4 S = mat4Scale(
649+ obj.scale.pos.x,
650+ obj.scale.pos.y,
651+ obj.scale.pos.z
652+ );
653+
654+ Matrix4 model = mat4Mul(T, mat4Mul(R, S));
655+
656+ depthProgram.SetUniformMat4("uModel", model);
657+
658+ VAO.BindVertexArray();
659+ VBO.BindBuffer();
660+ VBO.BufferData(obj.mesh.vertices.data(),
661+ obj.mesh.vertices.size() * sizeof(f32));
662+
663+ EBO.BindBuffer();
664+ EBO.BufferData(obj.mesh.indices.data(),
665+ obj.mesh.indices.size() * sizeof(GLuint));
666+
667+ glDrawElements(
668+ GL_TRIANGLES,
669+ obj.mesh.GetIndexCount(),
670+ GL_UNSIGNED_INT,
671+ nullptr
672+ );
673+ }
674+
675+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
676+#pragma endregion
677+
511678 // 画面をクリア
512679 glBindFramebuffer(GL_FRAMEBUFFER, viewport.GetFBO());
513680 glViewport(0, 0, viewport.GetWidth(), viewport.GetHeight());
@@ -516,6 +683,15 @@
516683
517684 // シェーダープログラムを使用
518685 shaderProgram.Use();
686+
687+#pragma region shadowmap
688+ shaderProgram.SetUniformMat4("uLightSpace", lightSpace);
689+
690+ glActiveTexture(GL_TEXTURE0);
691+ glBindTexture(GL_TEXTURE_2D, shadowMap);
692+ shaderProgram.SetUniformInt("uShadowMap", 0);
693+#pragma endregion
694+
519695 luaEngine.Init();
520696 luaEngine.Update();
521697
@@ -544,7 +720,6 @@
544720 );
545721
546722 Matrix4 model = mat4Mul(T, mat4Mul(R, S));
547-
548723 glUniformMatrix4fv(shaderProgram.GetUniformLocation("uModel"), 1, GL_FALSE, model.m);
549724
550725 // 描画
@@ -186,6 +186,8 @@
186186 createWord(db, 2, "filenew2dshader", "2D Shader");
187187 createWord(db, 1, "filenew3dshader", "3D シェーダー");
188188 createWord(db, 2, "filenew3dshader", "3D Shader");
189+ createWord(db, 1, "filenewsceneshader", "シーン シェーダー");
190+ createWord(db, 2, "filenewsceneshader", "Scene Shader");
189191 createWord(db, 1, "fileopen", "開く");
190192 createWord(db, 2, "fileopen", "Open");
191193 createWord(db, 1, "filesave", "保存");
@@ -48,13 +48,13 @@
4848 ImGui::Separator();
4949
5050 if (ImGui::Button("2D Shader", ImVec2(200, 0))) {
51- Make(false);
51+ Make(gui::ShaderType::Simple2D);
5252 m_Info->create();
5353 }
5454 }
5555 }
5656
57- db::ShaderData NewShader::Make(bool is3D) {
57+ db::ShaderData NewShader::Make(ShaderType type) {
5858 db::ShaderData newShader = {};
5959 auto curLang = m_Info->i18n->GetCurrentLanguage().code;
6060 newShader.id = 0;
@@ -64,19 +64,30 @@
6464
6565 newShader.vertexShader.id = 0;
6666 newShader.vertexShader.type = db::ShaderCodeType::GlslVertex;
67- newShader.vertexShader.code = is3D ? getDefaultVert3D() : getDefaultVert2D();
6867 newShader.vertexShader.filename = "";
6968
7069 newShader.fragmentShader.id = 0;
7170 newShader.fragmentShader.type = db::ShaderCodeType::GlslFragment;
72- newShader.fragmentShader.code = is3D ? getDefaultFrag3D() : getDefaultFrag2D();
7371 newShader.fragmentShader.filename = "";
7472
7573 newShader.luaCode.id = 0;
7674 newShader.luaCode.type = db::ShaderCodeType::Lua;
77- newShader.luaCode.code = is3D ? getDefaultLua3D() : getDefaultLua2D();
7875 newShader.luaCode.filename = "";
7976
77+ if (type == ShaderType::Scene) {
78+ newShader.vertexShader.code = getDefaultVertScene();
79+ newShader.fragmentShader.code = getDefaultFragScene();
80+ newShader.luaCode.code = getDefaultLuaScene();
81+ } else if (type == ShaderType::Simple3D) {
82+ newShader.vertexShader.code = getDefaultVert3D();
83+ newShader.fragmentShader.code = getDefaultFrag3D();
84+ newShader.luaCode.code = getDefaultLua3D();
85+ } else {
86+ newShader.vertexShader.code = getDefaultVert2D();
87+ newShader.fragmentShader.code = getDefaultFrag2D();
88+ newShader.luaCode.code = getDefaultLua2D();
89+ }
90+
8091 return newShader;
8192 }
8293
@@ -247,6 +258,342 @@
247258
248259 le.gfx.set_uniform_mat4("uView", view)
249260 le.gfx.set_uniform_mat4("uProjection", projection)
261+end)";
262+ }
263+
264+ string NewShader::getDefaultVertScene() {
265+ return R"(#version 460 core
266+
267+layout(location = 0) in vec3 aPosition;
268+layout(location = 1) in vec3 aNormal;
269+layout(location = 2) in vec3 aColor;
270+
271+out vec3 vFragPos;
272+out vec3 vNormal;
273+out vec3 vColor;
274+out vec4 vFragPosLightSpace;
275+
276+uniform mat4 uModel;
277+uniform mat4 uView;
278+uniform mat4 uProjection;
279+uniform mat4 uLightSpace;
280+
281+void main() {
282+ vec4 worldPos = uModel * vec4(aPosition, 1.0);
283+
284+ vFragPos = worldPos.xyz;
285+ vNormal = mat3(transpose(inverse(uModel))) * aNormal;
286+ vColor = aColor;
287+ vFragPosLightSpace = uLightSpace * worldPos;
288+
289+ gl_Position = uProjection * uView * worldPos;
290+})";
291+ }
292+
293+ string NewShader::getDefaultFragScene() {
294+ return R"(#version 460 core
295+
296+in vec3 vFragPos;
297+in vec3 vNormal;
298+in vec3 vColor;
299+in vec4 vFragPosLightSpace;
300+
301+out vec4 out_color;
302+
303+uniform vec3 uLightPos;
304+uniform vec3 uLightColor;
305+uniform sampler2D uShadowMap;
306+
307+void main() {
308+ vec3 norm = normalize(vNormal);
309+ vec3 lightDir = normalize(uLightPos - vFragPos);
310+
311+ float diff = max(dot(norm, lightDir), 0.0);
312+
313+ vec3 projCoords = vFragPosLightSpace.xyz / vFragPosLightSpace.w;
314+ projCoords = projCoords * 0.5 + 0.5;
315+
316+ float closestDepth = texture(uShadowMap, projCoords.xy).r;
317+ float currentDepth = projCoords.z;
318+
319+ float bias = max(0.05 * (1.0 - dot(norm, lightDir)), 0.005);
320+
321+ float shadow =
322+ (projCoords.z > 1.0)
323+ ? 0.0
324+ : (currentDepth - bias > closestDepth ? 1.0 : 0.0);
325+
326+ vec3 ambient = 0.2 * vColor;
327+ vec3 diffuse = (1.0 - shadow) * diff * uLightColor * vColor;
328+
329+ vec3 viewDir = normalize(-vFragPos);
330+ vec3 reflectDir = reflect(-lightDir, norm);
331+ float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
332+ vec3 specular = (1.0 - shadow) * 0.3 * spec * uLightColor;
333+
334+ vec3 result = ambient + diffuse + specular;
335+
336+ out_color = vec4(result, 1.0);
337+})";
338+ }
339+
340+ string NewShader::getDefaultLuaScene() {
341+ return R"(local posSize = le.sizeof("vec3")
342+local normalSize = le.sizeof("vec3")
343+local colorSize = le.sizeof("vec3")
344+
345+local stride = posSize + normalSize + colorSize
346+
347+local sphere = {
348+ vertices = {},
349+
350+ indices = {},
351+
352+ attributes = {
353+ { location = 0, size = 3, stride = stride, offset = 0 },
354+ { location = 1, size = 3, stride = stride, offset = posSize },
355+ { location = 2, size = 3, stride = stride, offset = posSize + normalSize }
356+ }
357+}
358+
359+local plane = {
360+ vertices = {
361+ -20, -2, -20, 0,1,0, 0.4, 0.4, 0.4,
362+ 20, -2, -20, 0,1,0, 0.4, 0.4, 0.4,
363+ 20, -2, 20, 0,1,0, 0.4, 0.4, 0.4,
364+ -20, -2, 20, 0,1,0, 0.4, 0.4, 0.4,
365+ },
366+ indices = {
367+ 0, 1, 2,
368+ 0, 2, 3,
369+ },
370+ attributes = {
371+ { location = 0, size = 3, stride = stride, offset = 0 },
372+ { location = 1, size = 3, stride = stride, offset = posSize },
373+ { location = 2, size = 3, stride = stride, offset = posSize + normalSize }
374+ }
375+}
376+
377+local pyramid = {
378+ vertices = {
379+ 0.0, 0.8, 0.0, 0.0, 0.447, 0.894, 1,1,1,
380+ -0.5, -0.2, 0.5, 0.0, 0.447, 0.894, 1,0,0,
381+ 0.5, -0.2, 0.5, 0.0, 0.447, 0.894, 0,1,0,
382+
383+ 0.0, 0.8, 0.0, 0.894, 0.447, 0.0, 1,1,1,
384+ 0.5, -0.2, 0.5, 0.894, 0.447, 0.0, 0,1,0,
385+ 0.5, -0.2, -0.5, 0.894, 0.447, 0.0, 0,0,1,
386+
387+ 0.0, 0.8, 0.0, 0.0, 0.447, -0.894, 1,1,1,
388+ 0.5, -0.2, -0.5, 0.0, 0.447, -0.894, 0,0,1,
389+ -0.5, -0.2, -0.5, 0.0, 0.447, -0.894, 1,1,0,
390+
391+ 0.0, 0.8, 0.0, -0.894, 0.447, 0.0, 1,1,1,
392+ -0.5, -0.2, -0.5, -0.894, 0.447, 0.0, 1,1,0,
393+ -0.5, -0.2, 0.5, -0.894, 0.447, 0.0, 1,0,0,
394+
395+ -0.5, -0.2, 0.5, 0,-1,0, 1,0,0,
396+ 0.5, -0.2, 0.5, 0,-1,0, 0,1,0,
397+ 0.5, -0.2, -0.5, 0,-1,0, 0,0,1,
398+ -0.5, -0.2, -0.5, 0,-1,0, 1,1,0,
399+ },
400+
401+ indices = {
402+ 0,1,2,
403+ 3,4,5,
404+ 6,7,8,
405+ 9,10,11,
406+
407+ 12,13,14,
408+ 12,14,15
409+ },
410+
411+ attributes = {
412+ { location = 0, size = 3, stride = stride, offset = 0 },
413+ { location = 1, size = 3, stride = stride, offset = posSize },
414+ { location = 2, size = 3, stride = stride, offset = posSize + normalSize }
415+ }
416+}
417+
418+local cube = {
419+ vertices = {
420+ -- position --normal -- uv
421+ -- mae
422+ -0.5, -0.5, -0.5, 0, 0, -1, 1,0,0,
423+ 0.5, -0.5, -0.5, 0, 0, -1, 0,1,0,
424+ 0.5, 0.5, -0.5, 0, 0, -1, 0,0,1,
425+ -0.5, 0.5, -0.5, 0, 0, -1, 1,1,0,
426+ -- usiro
427+ -0.5, -0.5, 0.5, 0, 0, 1, 1,0,1,
428+ 0.5, -0.5, 0.5, 0, 0, 1, 0,1,1,
429+ 0.5, 0.5, 0.5, 0, 0, 1, 1,1,1,
430+ -0.5, 0.5, 0.5, 0, 0, 1, 1,0,1,
431+ -- (migi)
432+ 0.5, -0.5, -0.5, 1, 0, 0, 0,1,0,
433+ 0.5, -0.5, 0.5, 1, 0, 0, 0,1,1,
434+ 0.5, 0.5, 0.5, 1, 0, 0, 1,1,1,
435+ 0.5, 0.5, -0.5, 1, 0, 0, 0,0,1,
436+ -- hidari
437+ -0.5, -0.5, 0.5, -1, 0, 0, 1,0,1,
438+ -0.5, -0.5, -0.5, -1, 0, 0, 1,0,0,
439+ -0.5, 0.5, -0.5, -1, 0, 0, 1,1,0,
440+ -0.5, 0.5, 0.5, -1, 0, 0, 1,0,1,
441+ -- ue
442+ -0.5, 0.5, -0.5, 0, 1, 0, 1,1,0,
443+ 0.5, 0.5, -0.5, 0, 1, 0, 0,0,1,
444+ 0.5, 0.5, 0.5, 0, 1, 0, 1,1,1,
445+ -0.5, 0.5, 0.5, 0, 1, 0, 1,0,1,
446+ -- sita
447+ -0.5, -0.5, -0.5, 0, -1, 0, 1,0,0,
448+ 0.5, -0.5, -0.5, 0, -1, 0, 0,1,0,
449+ 0.5, -0.5, 0.5, 0, -1, 0, 0,1,1,
450+ -0.5, -0.5, 0.5, 0, -1, 0, 1,0,1,
451+ },
452+
453+ indices = {
454+ 0,1,2, 2,3,0,
455+ 4,5,6, 6,7,4,
456+ 8,9,10, 10,11,8,
457+ 12,13,14, 14,15,12,
458+ 16,17,18, 18,19,16,
459+ 20,21,22, 22,23,20
460+ },
461+
462+ attributes = {
463+ { location = 0, size = 3, stride = stride, offset = 0 },
464+ { location = 1, size = 3, stride = stride, offset = posSize },
465+ { location = 2, size = 3, stride = stride, offset = posSize + normalSize }
466+ }
467+}
468+
469+local radius = .5
470+local stacks = 16
471+local slices = 24
472+
473+for i = 0, stacks do
474+ local phi = math.pi * i / stacks
475+ local y = radius * math.cos(phi)
476+ local ringRadius = radius * math.sin(phi)
477+
478+ for j = 0, slices -1 do
479+ local theta = 2 * math.pi * j / slices
480+
481+ local x = ringRadius * math.cos(theta)
482+ local z = ringRadius * math.sin(theta)
483+
484+ local len = math.sqrt(x*x + y*y + z*z)
485+ local nx = x / len
486+ local ny = y / len
487+ local nz = z / len
488+
489+ local r = (x + radius) / (2 * radius)
490+ local g = (y + radius) / (2 * radius)
491+ local b = (z + radius) / (2 * radius)
492+
493+ table.insert(sphere.vertices, x)
494+ table.insert(sphere.vertices, y)
495+ table.insert(sphere.vertices, z)
496+
497+ table.insert(sphere.vertices, nx)
498+ table.insert(sphere.vertices, ny)
499+ table.insert(sphere.vertices, nz)
500+
501+ table.insert(sphere.vertices, r)
502+ table.insert(sphere.vertices, g)
503+ table.insert(sphere.vertices, b)
504+ end
505+end
506+
507+for i = 0, stacks - 1 do
508+ for j = 0, slices - 1 do
509+ local nextJ = (j + 1) % slices
510+
511+ local a = i * slices + j
512+ local b = (i + 1) * slices + j
513+ local c = i * slices + nextJ
514+ local d = (i + 1) * slices + nextJ
515+
516+ table.insert(sphere.indices, a)
517+ table.insert(sphere.indices, b)
518+ table.insert(sphere.indices, c)
519+
520+ table.insert(sphere.indices, c)
521+ table.insert(sphere.indices, b)
522+ table.insert(sphere.indices, d)
523+ end
524+end
525+
526+le.gfx.add_mesh(cube)
527+le.gfx.set_position(0, 0, 0, -1)
528+le.gfx.add_mesh(cube)
529+le.gfx.set_position(1, -8, -2, -11)
530+le.gfx.add_mesh(cube)
531+le.gfx.set_position(2, 8, -2, -11)
532+
533+le.gfx.add_mesh(pyramid)
534+le.gfx.set_position(3, 8, 2, -11)
535+le.gfx.set_scale(3, 2, 2, 2)
536+
537+le.gfx.add_mesh(sphere)
538+le.gfx.set_position(4, -2, 1, -3)
539+
540+le.gfx.add_mesh(plane)
541+le.gfx.set_position(5, 0, -2, 0)
542+
543+function init()
544+end
545+
546+function update()
547+ local t = le.sys.get_time()
548+ local zs = -3 + math.sin(t * 1.5) * 6
549+ le.gfx.set_position(4, -2.3, -1, zs)
550+
551+ local size = 8
552+ local speed = 0.8
553+ local phase = (t * speed) % (4 * math.pi)
554+ local cubeX, cubeY
555+
556+ if phase < math.pi then
557+ cubeX = -size/2 + (phase / math.pi) * size
558+ cubeY = size/2
559+ elseif phase < 2 * math.pi then
560+ cubeX = size/2
561+ cubeY = size/2 - ((phase - math.pi) / math.pi) * size
562+ elseif phase < 3 * math.pi then
563+ cubeX = size/2 - ((phase - 2*math.pi) / math.pi) * size
564+ cubeY = -size/2
565+ else
566+ cubeX = -size/2
567+ cubeY = -size/2 + ((phase - 3*math.pi) / math.pi) * size
568+ end
569+
570+ le.gfx.set_position(2, cubeX, cubeY, -11)
571+
572+ le.gfx.set_rotation(0, t*0.7, t*1.0, t*0.4)
573+ le.gfx.set_rotation(1, t*0.2, t*0.8, t*1.4)
574+ le.gfx.set_rotation(2, t*0.4, t*0.5, t*0.8)
575+ le.gfx.set_rotation(3, 0, t*0.8, -.5)
576+
577+ local reso = le.sys.get_resolution()
578+ local aspect = reso.x / reso.y
579+
580+ local view = le.math.look_at(
581+ 0, 0, 4, -- eye
582+ 0, 0, 0, -- target
583+ 0, 1, 0 -- up
584+ )
585+
586+ local projection = le.math.perspective(
587+ 45.0,
588+ aspect,
589+ 0.1,
590+ 100.0
591+ )
592+
593+ le.gfx.set_uniform_mat4("uView", view)
594+ le.gfx.set_uniform_mat4("uProjection", projection)
595+ le.gfx.set_uniform_vec3("uLightPos", 3, 5, 2)
596+ le.gfx.set_uniform_vec3("uLightColor", 1, 1, 1)
250597 end)";
251598 }
252599 } // namespace gui
@@ -866,6 +866,10 @@
866866 glUniform4ui(loc, x, y, z, w);
867867 };
868868
869+ lua["le"]["gfx"]["set_uniform_vec3"] = [&](const string &name, f32 x, f32 y, f32 z) {
870+ m_Program->SetUniformVec3(name, { x, y, z });
871+ };
872+
869873 lua["le"]["gfx"]["set_uniform_mat4"] = [&](const string &name, sol::table t) {
870874 if (!m_Program) return;
871875 if (t.size() != 16) {
@@ -103,6 +103,20 @@
103103 return glGetUniformLocation(id, var.c_str());
104104 }
105105
106+void Program::SetUniformVec3(const string &name, Vector3 v) {
107+ glUniform3f(GetUniformLocation(name), v.pos.x, v.pos.y, v.pos.z);
108+}
109+
110+void Program::SetUniformMat4(const string &name, Matrix4 v) {
111+ glUniformMatrix4fv(GetUniformLocation(name), 1, GL_FALSE, v.m);
112+}
113+
114+void Program::SetUniformInt(const string &name, i32 v) {
115+ glUniform1i(GetUniformLocation(name), v);
116+}
117+
118+/////////////////
119+
106120 string GetDefaultVertexShader() {
107121 return R"(#version 460 core
108122