summaryrefslogtreecommitdiff
path: root/src/video_core/renderer_opengl
diff options
context:
space:
mode:
authorGravatar bunnei2016-02-05 21:49:44 -0500
committerGravatar bunnei2016-02-05 21:49:44 -0500
commitf1d1049c4fdf51fd382f9afb6b5cd5f104e71b0e (patch)
tree5cac94917f52f018e0939f8b2e1be079e50c313c /src/video_core/renderer_opengl
parentMerge pull request #1391 from tfarley/hw-fb-sync-fix (diff)
parentpica: Cleanup lighting register definitions and documentation. (diff)
downloadyuzu-f1d1049c4fdf51fd382f9afb6b5cd5f104e71b0e.tar.gz
yuzu-f1d1049c4fdf51fd382f9afb6b5cd5f104e71b0e.tar.xz
yuzu-f1d1049c4fdf51fd382f9afb6b5cd5f104e71b0e.zip
Merge pull request #1264 from bunnei/fragment-lighting-hw
Fragment lighting support in the HW renderer
Diffstat (limited to 'src/video_core/renderer_opengl')
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp325
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h147
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp219
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h2
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_state.h4
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h12
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp4
8 files changed, 692 insertions, 29 deletions
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 6441e2586..b7d19bf94 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -75,6 +75,12 @@ void RasterizerOpenGL::InitObjects() {
75 glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1); 75 glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1);
76 glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2); 76 glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2);
77 77
78 glVertexAttribPointer(GLShader::ATTRIBUTE_NORMQUAT, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, normquat));
79 glEnableVertexAttribArray(GLShader::ATTRIBUTE_NORMQUAT);
80
81 glVertexAttribPointer(GLShader::ATTRIBUTE_VIEW, 3, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, view));
82 glEnableVertexAttribArray(GLShader::ATTRIBUTE_VIEW);
83
78 SetShader(); 84 SetShader();
79 85
80 // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation 86 // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
@@ -120,6 +126,19 @@ void RasterizerOpenGL::InitObjects() {
120 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.handle, 0); 126 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.handle, 0);
121 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0); 127 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0);
122 128
129 for (size_t i = 0; i < lighting_lut.size(); ++i) {
130 lighting_lut[i].Create();
131 state.lighting_lut[i].texture_1d = lighting_lut[i].handle;
132
133 glActiveTexture(GL_TEXTURE3 + i);
134 glBindTexture(GL_TEXTURE_1D, state.lighting_lut[i].texture_1d);
135
136 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
137 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
138 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
139 }
140 state.Apply();
141
123 ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, 142 ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
124 "OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER)); 143 "OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER));
125} 144}
@@ -139,12 +158,34 @@ void RasterizerOpenGL::Reset() {
139 res_cache.InvalidateAll(); 158 res_cache.InvalidateAll();
140} 159}
141 160
161/**
162 * This is a helper function to resolve an issue with opposite quaternions being interpolated by
163 * OpenGL. See below for a detailed description of this issue (yuriks):
164 *
165 * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you
166 * interpolate two quaternions that are opposite, instead of going from one rotation to another
167 * using the shortest path, you'll go around the longest path. You can test if two quaternions are
168 * opposite by checking if Dot(Q1, W2) < 0. In that case, you can flip either of them, therefore
169 * making Dot(-Q1, W2) positive.
170 *
171 * NOTE: This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This
172 * should be correct for nearly all cases, however a more correct implementation (but less trivial
173 * and perhaps unnecessary) would be to handle this per-fragment, by interpolating the quaternions
174 * manually using two Lerps, and doing this correction before each Lerp.
175 */
176static bool AreQuaternionsOpposite(Math::Vec4<Pica::float24> qa, Math::Vec4<Pica::float24> qb) {
177 Math::Vec4f a{ qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32() };
178 Math::Vec4f b{ qb.x.ToFloat32(), qb.y.ToFloat32(), qb.z.ToFloat32(), qb.w.ToFloat32() };
179
180 return (Math::Dot(a, b) < 0.f);
181}
182
142void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0, 183void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0,
143 const Pica::Shader::OutputVertex& v1, 184 const Pica::Shader::OutputVertex& v1,
144 const Pica::Shader::OutputVertex& v2) { 185 const Pica::Shader::OutputVertex& v2) {
145 vertex_batch.emplace_back(v0); 186 vertex_batch.emplace_back(v0, false);
146 vertex_batch.emplace_back(v1); 187 vertex_batch.emplace_back(v1, AreQuaternionsOpposite(v0.quat, v1.quat));
147 vertex_batch.emplace_back(v2); 188 vertex_batch.emplace_back(v2, AreQuaternionsOpposite(v0.quat, v2.quat));
148} 189}
149 190
150void RasterizerOpenGL::DrawTriangles() { 191void RasterizerOpenGL::DrawTriangles() {
@@ -156,6 +197,13 @@ void RasterizerOpenGL::DrawTriangles() {
156 state.draw.shader_dirty = false; 197 state.draw.shader_dirty = false;
157 } 198 }
158 199
200 for (unsigned index = 0; index < lighting_lut.size(); index++) {
201 if (uniform_block_data.lut_dirty[index]) {
202 SyncLightingLUT(index);
203 uniform_block_data.lut_dirty[index] = false;
204 }
205 }
206
159 if (uniform_block_data.dirty) { 207 if (uniform_block_data.dirty) {
160 glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, GL_STATIC_DRAW); 208 glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, GL_STATIC_DRAW);
161 uniform_block_data.dirty = false; 209 uniform_block_data.dirty = false;
@@ -283,6 +331,165 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
283 case PICA_REG_INDEX(tev_combiner_buffer_color): 331 case PICA_REG_INDEX(tev_combiner_buffer_color):
284 SyncCombinerColor(); 332 SyncCombinerColor();
285 break; 333 break;
334
335 // Fragment lighting specular 0 color
336 case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_0, 0x140 + 0 * 0x10):
337 SyncLightSpecular0(0);
338 break;
339 case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_0, 0x140 + 1 * 0x10):
340 SyncLightSpecular0(1);
341 break;
342 case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_0, 0x140 + 2 * 0x10):
343 SyncLightSpecular0(2);
344 break;
345 case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_0, 0x140 + 3 * 0x10):
346 SyncLightSpecular0(3);
347 break;
348 case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_0, 0x140 + 4 * 0x10):
349 SyncLightSpecular0(4);
350 break;
351 case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_0, 0x140 + 5 * 0x10):
352 SyncLightSpecular0(5);
353 break;
354 case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_0, 0x140 + 6 * 0x10):
355 SyncLightSpecular0(6);
356 break;
357 case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_0, 0x140 + 7 * 0x10):
358 SyncLightSpecular0(7);
359 break;
360
361 // Fragment lighting specular 1 color
362 case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_1, 0x141 + 0 * 0x10):
363 SyncLightSpecular1(0);
364 break;
365 case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_1, 0x141 + 1 * 0x10):
366 SyncLightSpecular1(1);
367 break;
368 case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_1, 0x141 + 2 * 0x10):
369 SyncLightSpecular1(2);
370 break;
371 case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_1, 0x141 + 3 * 0x10):
372 SyncLightSpecular1(3);
373 break;
374 case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_1, 0x141 + 4 * 0x10):
375 SyncLightSpecular1(4);
376 break;
377 case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_1, 0x141 + 5 * 0x10):
378 SyncLightSpecular1(5);
379 break;
380 case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_1, 0x141 + 6 * 0x10):
381 SyncLightSpecular1(6);
382 break;
383 case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_1, 0x141 + 7 * 0x10):
384 SyncLightSpecular1(7);
385 break;
386
387 // Fragment lighting diffuse color
388 case PICA_REG_INDEX_WORKAROUND(lighting.light[0].diffuse, 0x142 + 0 * 0x10):
389 SyncLightDiffuse(0);
390 break;
391 case PICA_REG_INDEX_WORKAROUND(lighting.light[1].diffuse, 0x142 + 1 * 0x10):
392 SyncLightDiffuse(1);
393 break;
394 case PICA_REG_INDEX_WORKAROUND(lighting.light[2].diffuse, 0x142 + 2 * 0x10):
395 SyncLightDiffuse(2);
396 break;
397 case PICA_REG_INDEX_WORKAROUND(lighting.light[3].diffuse, 0x142 + 3 * 0x10):
398 SyncLightDiffuse(3);
399 break;
400 case PICA_REG_INDEX_WORKAROUND(lighting.light[4].diffuse, 0x142 + 4 * 0x10):
401 SyncLightDiffuse(4);
402 break;
403 case PICA_REG_INDEX_WORKAROUND(lighting.light[5].diffuse, 0x142 + 5 * 0x10):
404 SyncLightDiffuse(5);
405 break;
406 case PICA_REG_INDEX_WORKAROUND(lighting.light[6].diffuse, 0x142 + 6 * 0x10):
407 SyncLightDiffuse(6);
408 break;
409 case PICA_REG_INDEX_WORKAROUND(lighting.light[7].diffuse, 0x142 + 7 * 0x10):
410 SyncLightDiffuse(7);
411 break;
412
413 // Fragment lighting ambient color
414 case PICA_REG_INDEX_WORKAROUND(lighting.light[0].ambient, 0x143 + 0 * 0x10):
415 SyncLightAmbient(0);
416 break;
417 case PICA_REG_INDEX_WORKAROUND(lighting.light[1].ambient, 0x143 + 1 * 0x10):
418 SyncLightAmbient(1);
419 break;
420 case PICA_REG_INDEX_WORKAROUND(lighting.light[2].ambient, 0x143 + 2 * 0x10):
421 SyncLightAmbient(2);
422 break;
423 case PICA_REG_INDEX_WORKAROUND(lighting.light[3].ambient, 0x143 + 3 * 0x10):
424 SyncLightAmbient(3);
425 break;
426 case PICA_REG_INDEX_WORKAROUND(lighting.light[4].ambient, 0x143 + 4 * 0x10):
427 SyncLightAmbient(4);
428 break;
429 case PICA_REG_INDEX_WORKAROUND(lighting.light[5].ambient, 0x143 + 5 * 0x10):
430 SyncLightAmbient(5);
431 break;
432 case PICA_REG_INDEX_WORKAROUND(lighting.light[6].ambient, 0x143 + 6 * 0x10):
433 SyncLightAmbient(6);
434 break;
435 case PICA_REG_INDEX_WORKAROUND(lighting.light[7].ambient, 0x143 + 7 * 0x10):
436 SyncLightAmbient(7);
437 break;
438
439 // Fragment lighting position
440 case PICA_REG_INDEX_WORKAROUND(lighting.light[0].x, 0x144 + 0 * 0x10):
441 case PICA_REG_INDEX_WORKAROUND(lighting.light[0].z, 0x145 + 0 * 0x10):
442 SyncLightPosition(0);
443 break;
444 case PICA_REG_INDEX_WORKAROUND(lighting.light[1].x, 0x144 + 1 * 0x10):
445 case PICA_REG_INDEX_WORKAROUND(lighting.light[1].z, 0x145 + 1 * 0x10):
446 SyncLightPosition(1);
447 break;
448 case PICA_REG_INDEX_WORKAROUND(lighting.light[2].x, 0x144 + 2 * 0x10):
449 case PICA_REG_INDEX_WORKAROUND(lighting.light[2].z, 0x145 + 2 * 0x10):
450 SyncLightPosition(2);
451 break;
452 case PICA_REG_INDEX_WORKAROUND(lighting.light[3].x, 0x144 + 3 * 0x10):
453 case PICA_REG_INDEX_WORKAROUND(lighting.light[3].z, 0x145 + 3 * 0x10):
454 SyncLightPosition(3);
455 break;
456 case PICA_REG_INDEX_WORKAROUND(lighting.light[4].x, 0x144 + 4 * 0x10):
457 case PICA_REG_INDEX_WORKAROUND(lighting.light[4].z, 0x145 + 4 * 0x10):
458 SyncLightPosition(4);
459 break;
460 case PICA_REG_INDEX_WORKAROUND(lighting.light[5].x, 0x144 + 5 * 0x10):
461 case PICA_REG_INDEX_WORKAROUND(lighting.light[5].z, 0x145 + 5 * 0x10):
462 SyncLightPosition(5);
463 break;
464 case PICA_REG_INDEX_WORKAROUND(lighting.light[6].x, 0x144 + 6 * 0x10):
465 case PICA_REG_INDEX_WORKAROUND(lighting.light[6].z, 0x145 + 6 * 0x10):
466 SyncLightPosition(6);
467 break;
468 case PICA_REG_INDEX_WORKAROUND(lighting.light[7].x, 0x144 + 7 * 0x10):
469 case PICA_REG_INDEX_WORKAROUND(lighting.light[7].z, 0x145 + 7 * 0x10):
470 SyncLightPosition(7);
471 break;
472
473 // Fragment lighting global ambient color (emission + ambient * ambient)
474 case PICA_REG_INDEX_WORKAROUND(lighting.global_ambient, 0x1c0):
475 SyncGlobalAmbient();
476 break;
477
478 // Fragment lighting lookup tables
479 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[0], 0x1c8):
480 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[1], 0x1c9):
481 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[2], 0x1ca):
482 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[3], 0x1cb):
483 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[4], 0x1cc):
484 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[5], 0x1cd):
485 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce):
486 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf):
487 {
488 auto& lut_config = regs.lighting.lut_config;
489 uniform_block_data.lut_dirty[lut_config.type / 4] = true;
490 break;
491 }
492
286 } 493 }
287} 494}
288 495
@@ -491,18 +698,39 @@ void RasterizerOpenGL::SetShader() {
491 uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]"); 698 uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]");
492 if (uniform_tex != -1) { glUniform1i(uniform_tex, 2); } 699 if (uniform_tex != -1) { glUniform1i(uniform_tex, 2); }
493 700
701 // Set the texture samplers to correspond to different lookup table texture units
702 GLuint uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[0]");
703 if (uniform_lut != -1) { glUniform1i(uniform_lut, 3); }
704 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[1]");
705 if (uniform_lut != -1) { glUniform1i(uniform_lut, 4); }
706 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[2]");
707 if (uniform_lut != -1) { glUniform1i(uniform_lut, 5); }
708 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[3]");
709 if (uniform_lut != -1) { glUniform1i(uniform_lut, 6); }
710 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[4]");
711 if (uniform_lut != -1) { glUniform1i(uniform_lut, 7); }
712 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]");
713 if (uniform_lut != -1) { glUniform1i(uniform_lut, 8); }
714
494 current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); 715 current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
495 716
496 unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data"); 717 unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
497 glUniformBlockBinding(current_shader->shader.handle, block_index, 0); 718 glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
498 }
499 719
500 // Update uniforms 720 // Update uniforms
501 SyncAlphaTest(); 721 SyncAlphaTest();
502 SyncCombinerColor(); 722 SyncCombinerColor();
503 auto& tev_stages = Pica::g_state.regs.GetTevStages(); 723 auto& tev_stages = Pica::g_state.regs.GetTevStages();
504 for (int index = 0; index < tev_stages.size(); ++index) 724 for (int index = 0; index < tev_stages.size(); ++index)
505 SyncTevConstColor(index, tev_stages[index]); 725 SyncTevConstColor(index, tev_stages[index]);
726
727 SyncGlobalAmbient();
728 for (int light_index = 0; light_index < 8; light_index++) {
729 SyncLightDiffuse(light_index);
730 SyncLightAmbient(light_index);
731 SyncLightPosition(light_index);
732 }
733 }
506} 734}
507 735
508void RasterizerOpenGL::SyncFramebuffer() { 736void RasterizerOpenGL::SyncFramebuffer() {
@@ -604,8 +832,8 @@ void RasterizerOpenGL::SyncCullMode() {
604} 832}
605 833
606void RasterizerOpenGL::SyncDepthModifiers() { 834void RasterizerOpenGL::SyncDepthModifiers() {
607 float depth_scale = -Pica::float24::FromRawFloat24(Pica::g_state.regs.viewport_depth_range).ToFloat32(); 835 float depth_scale = -Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32();
608 float depth_offset = Pica::float24::FromRawFloat24(Pica::g_state.regs.viewport_depth_far_plane).ToFloat32() / 2.0f; 836 float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_far_plane).ToFloat32() / 2.0f;
609 837
610 // TODO: Implement scale modifier 838 // TODO: Implement scale modifier
611 uniform_block_data.data.depth_offset = depth_offset; 839 uniform_block_data.data.depth_offset = depth_offset;
@@ -683,12 +911,81 @@ void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevS
683 } 911 }
684} 912}
685 913
914void RasterizerOpenGL::SyncGlobalAmbient() {
915 auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.global_ambient);
916 if (color != uniform_block_data.data.lighting_global_ambient) {
917 uniform_block_data.data.lighting_global_ambient = color;
918 uniform_block_data.dirty = true;
919 }
920}
921
922void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) {
923 std::array<GLvec4, 256> new_data;
924
925 for (unsigned offset = 0; offset < new_data.size(); ++offset) {
926 new_data[offset][0] = Pica::g_state.lighting.luts[(lut_index * 4) + 0][offset].ToFloat();
927 new_data[offset][1] = Pica::g_state.lighting.luts[(lut_index * 4) + 1][offset].ToFloat();
928 new_data[offset][2] = Pica::g_state.lighting.luts[(lut_index * 4) + 2][offset].ToFloat();
929 new_data[offset][3] = Pica::g_state.lighting.luts[(lut_index * 4) + 3][offset].ToFloat();
930 }
931
932 if (new_data != lighting_lut_data[lut_index]) {
933 lighting_lut_data[lut_index] = new_data;
934 glActiveTexture(GL_TEXTURE3 + lut_index);
935 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, lighting_lut_data[lut_index].data());
936 }
937}
938
939void RasterizerOpenGL::SyncLightSpecular0(int light_index) {
940 auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_0);
941 if (color != uniform_block_data.data.light_src[light_index].specular_0) {
942 uniform_block_data.data.light_src[light_index].specular_0 = color;
943 uniform_block_data.dirty = true;
944 }
945}
946
947void RasterizerOpenGL::SyncLightSpecular1(int light_index) {
948 auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_1);
949 if (color != uniform_block_data.data.light_src[light_index].specular_1) {
950 uniform_block_data.data.light_src[light_index].specular_1 = color;
951 uniform_block_data.dirty = true;
952 }
953}
954
955void RasterizerOpenGL::SyncLightDiffuse(int light_index) {
956 auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].diffuse);
957 if (color != uniform_block_data.data.light_src[light_index].diffuse) {
958 uniform_block_data.data.light_src[light_index].diffuse = color;
959 uniform_block_data.dirty = true;
960 }
961}
962
963void RasterizerOpenGL::SyncLightAmbient(int light_index) {
964 auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].ambient);
965 if (color != uniform_block_data.data.light_src[light_index].ambient) {
966 uniform_block_data.data.light_src[light_index].ambient = color;
967 uniform_block_data.dirty = true;
968 }
969}
970
971void RasterizerOpenGL::SyncLightPosition(int light_index) {
972 GLvec3 position = {
973 Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].x).ToFloat32(),
974 Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].y).ToFloat32(),
975 Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].z).ToFloat32() };
976
977 if (position != uniform_block_data.data.light_src[light_index].position) {
978 uniform_block_data.data.light_src[light_index].position = position;
979 uniform_block_data.dirty = true;
980 }
981}
982
686void RasterizerOpenGL::SyncDrawState() { 983void RasterizerOpenGL::SyncDrawState() {
687 const auto& regs = Pica::g_state.regs; 984 const auto& regs = Pica::g_state.regs;
688 985
689 // Sync the viewport 986 // Sync the viewport
690 GLsizei viewport_width = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_x).ToFloat32() * 2; 987 GLsizei viewport_width = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_x).ToFloat32() * 2;
691 GLsizei viewport_height = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_y).ToFloat32() * 2; 988 GLsizei viewport_height = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_y).ToFloat32() * 2;
692 989
693 // OpenGL uses different y coordinates, so negate corner offset and flip origin 990 // OpenGL uses different y coordinates, so negate corner offset and flip origin
694 // TODO: Ensure viewport_corner.x should not be negated or origin flipped 991 // TODO: Ensure viewport_corner.x should not be negated or origin flipped
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 569beaa5c..fef5f5331 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -17,6 +17,7 @@
17#include "video_core/rasterizer_interface.h" 17#include "video_core/rasterizer_interface.h"
18#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 18#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
19#include "video_core/renderer_opengl/gl_state.h" 19#include "video_core/renderer_opengl/gl_state.h"
20#include "video_core/renderer_opengl/pica_to_gl.h"
20#include "video_core/shader/shader_interpreter.h" 21#include "video_core/shader/shader_interpreter.h"
21 22
22/** 23/**
@@ -71,6 +72,59 @@ struct PicaShaderConfig {
71 regs.tev_combiner_buffer_input.update_mask_rgb.Value() | 72 regs.tev_combiner_buffer_input.update_mask_rgb.Value() |
72 regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; 73 regs.tev_combiner_buffer_input.update_mask_a.Value() << 4;
73 74
75 // Fragment lighting
76
77 res.lighting.enable = !regs.lighting.disable;
78 res.lighting.src_num = regs.lighting.num_lights + 1;
79
80 for (unsigned light_index = 0; light_index < res.lighting.src_num; ++light_index) {
81 unsigned num = regs.lighting.light_enable.GetNum(light_index);
82 const auto& light = regs.lighting.light[num];
83 res.lighting.light[light_index].num = num;
84 res.lighting.light[light_index].directional = light.directional != 0;
85 res.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0;
86 res.lighting.light[light_index].dist_atten_enable = !regs.lighting.IsDistAttenDisabled(num);
87 res.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRaw(light.dist_atten_bias).ToFloat32();
88 res.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRaw(light.dist_atten_scale).ToFloat32();
89 }
90
91 res.lighting.lut_d0.enable = regs.lighting.disable_lut_d0 == 0;
92 res.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0;
93 res.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value();
94 res.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
95
96 res.lighting.lut_d1.enable = regs.lighting.disable_lut_d1 == 0;
97 res.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0;
98 res.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value();
99 res.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
100
101 res.lighting.lut_fr.enable = regs.lighting.disable_lut_fr == 0;
102 res.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0;
103 res.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value();
104 res.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
105
106 res.lighting.lut_rr.enable = regs.lighting.disable_lut_rr == 0;
107 res.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0;
108 res.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value();
109 res.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
110
111 res.lighting.lut_rg.enable = regs.lighting.disable_lut_rg == 0;
112 res.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0;
113 res.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value();
114 res.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
115
116 res.lighting.lut_rb.enable = regs.lighting.disable_lut_rb == 0;
117 res.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0;
118 res.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value();
119 res.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
120
121 res.lighting.config = regs.lighting.config;
122 res.lighting.fresnel_selector = regs.lighting.fresnel_selector;
123 res.lighting.bump_mode = regs.lighting.bump_mode;
124 res.lighting.bump_selector = regs.lighting.bump_selector;
125 res.lighting.bump_renorm = regs.lighting.disable_bump_renorm == 0;
126 res.lighting.clamp_highlights = regs.lighting.clamp_highlights != 0;
127
74 return res; 128 return res;
75 } 129 }
76 130
@@ -86,9 +140,37 @@ struct PicaShaderConfig {
86 return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0; 140 return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0;
87 }; 141 };
88 142
89 Pica::Regs::CompareFunc alpha_test_func; 143 Pica::Regs::CompareFunc alpha_test_func = Pica::Regs::CompareFunc::Never;
90 std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {}; 144 std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {};
91 u8 combiner_buffer_input; 145 u8 combiner_buffer_input = 0;
146
147 struct {
148 struct {
149 unsigned num = 0;
150 bool directional = false;
151 bool two_sided_diffuse = false;
152 bool dist_atten_enable = false;
153 GLfloat dist_atten_scale = 0.0f;
154 GLfloat dist_atten_bias = 0.0f;
155 } light[8];
156
157 bool enable = false;
158 unsigned src_num = 0;
159 Pica::Regs::LightingBumpMode bump_mode = Pica::Regs::LightingBumpMode::None;
160 unsigned bump_selector = 0;
161 bool bump_renorm = false;
162 bool clamp_highlights = false;
163
164 Pica::Regs::LightingConfig config = Pica::Regs::LightingConfig::Config0;
165 Pica::Regs::LightingFresnelSelector fresnel_selector = Pica::Regs::LightingFresnelSelector::None;
166
167 struct {
168 bool enable = false;
169 bool abs_input = false;
170 Pica::Regs::LightingLutInput type = Pica::Regs::LightingLutInput::NH;
171 float scale = 1.0f;
172 } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb;
173 } lighting;
92}; 174};
93 175
94namespace std { 176namespace std {
@@ -167,7 +249,7 @@ private:
167 249
168 /// Structure that the hardware rendered vertices are composed of 250 /// Structure that the hardware rendered vertices are composed of
169 struct HardwareVertex { 251 struct HardwareVertex {
170 HardwareVertex(const Pica::Shader::OutputVertex& v) { 252 HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) {
171 position[0] = v.pos.x.ToFloat32(); 253 position[0] = v.pos.x.ToFloat32();
172 position[1] = v.pos.y.ToFloat32(); 254 position[1] = v.pos.y.ToFloat32();
173 position[2] = v.pos.z.ToFloat32(); 255 position[2] = v.pos.z.ToFloat32();
@@ -182,6 +264,19 @@ private:
182 tex_coord1[1] = v.tc1.y.ToFloat32(); 264 tex_coord1[1] = v.tc1.y.ToFloat32();
183 tex_coord2[0] = v.tc2.x.ToFloat32(); 265 tex_coord2[0] = v.tc2.x.ToFloat32();
184 tex_coord2[1] = v.tc2.y.ToFloat32(); 266 tex_coord2[1] = v.tc2.y.ToFloat32();
267 normquat[0] = v.quat.x.ToFloat32();
268 normquat[1] = v.quat.y.ToFloat32();
269 normquat[2] = v.quat.z.ToFloat32();
270 normquat[3] = v.quat.w.ToFloat32();
271 view[0] = v.view.x.ToFloat32();
272 view[1] = v.view.y.ToFloat32();
273 view[2] = v.view.z.ToFloat32();
274
275 if (flip_quaternion) {
276 for (float& x : normquat) {
277 x = -x;
278 }
279 }
185 } 280 }
186 281
187 GLfloat position[4]; 282 GLfloat position[4];
@@ -189,20 +284,31 @@ private:
189 GLfloat tex_coord0[2]; 284 GLfloat tex_coord0[2];
190 GLfloat tex_coord1[2]; 285 GLfloat tex_coord1[2];
191 GLfloat tex_coord2[2]; 286 GLfloat tex_coord2[2];
287 GLfloat normquat[4];
288 GLfloat view[3];
289 };
290
291 struct LightSrc {
292 alignas(16) GLvec3 specular_0;
293 alignas(16) GLvec3 specular_1;
294 alignas(16) GLvec3 diffuse;
295 alignas(16) GLvec3 ambient;
296 alignas(16) GLvec3 position;
192 }; 297 };
193 298
194 /// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned 299 /// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned
195 struct UniformData { 300 struct UniformData {
196 // A vec4 color for each of the six tev stages 301 // A vec4 color for each of the six tev stages
197 std::array<GLfloat, 4> const_color[6]; 302 GLvec4 const_color[6];
198 std::array<GLfloat, 4> tev_combiner_buffer_color; 303 GLvec4 tev_combiner_buffer_color;
199 GLint alphatest_ref; 304 GLint alphatest_ref;
200 GLfloat depth_offset; 305 GLfloat depth_offset;
201 INSERT_PADDING_BYTES(8); 306 alignas(16) GLvec3 lighting_global_ambient;
307 LightSrc light_src[8];
202 }; 308 };
203 309
204 static_assert(sizeof(UniformData) == 0x80, "The size of the UniformData structure has changed, update the structure in the shader"); 310 static_assert(sizeof(UniformData) == 0x310, "The size of the UniformData structure has changed, update the structure in the shader");
205 static_assert(sizeof(UniformData) < 16000, "UniformData structure must be less than 16kb as per the OpenGL spec"); 311 static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec");
206 312
207 /// Reconfigure the OpenGL color texture to use the given format and dimensions 313 /// Reconfigure the OpenGL color texture to use the given format and dimensions
208 void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height); 314 void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height);
@@ -249,6 +355,27 @@ private:
249 /// Syncs the TEV combiner color buffer to match the PICA register 355 /// Syncs the TEV combiner color buffer to match the PICA register
250 void SyncCombinerColor(); 356 void SyncCombinerColor();
251 357
358 /// Syncs the lighting global ambient color to match the PICA register
359 void SyncGlobalAmbient();
360
361 /// Syncs the lighting lookup tables
362 void SyncLightingLUT(unsigned index);
363
364 /// Syncs the specified light's diffuse color to match the PICA register
365 void SyncLightDiffuse(int light_index);
366
367 /// Syncs the specified light's ambient color to match the PICA register
368 void SyncLightAmbient(int light_index);
369
370 /// Syncs the specified light's position to match the PICA register
371 void SyncLightPosition(int light_index);
372
373 /// Syncs the specified light's specular 0 color to match the PICA register
374 void SyncLightSpecular0(int light_index);
375
376 /// Syncs the specified light's specular 1 color to match the PICA register
377 void SyncLightSpecular1(int light_index);
378
252 /// Syncs the remaining OpenGL drawing state to match the current PICA state 379 /// Syncs the remaining OpenGL drawing state to match the current PICA state
253 void SyncDrawState(); 380 void SyncDrawState();
254 381
@@ -291,6 +418,7 @@ private:
291 418
292 struct { 419 struct {
293 UniformData data; 420 UniformData data;
421 bool lut_dirty[6];
294 bool dirty; 422 bool dirty;
295 } uniform_block_data; 423 } uniform_block_data;
296 424
@@ -298,4 +426,7 @@ private:
298 OGLBuffer vertex_buffer; 426 OGLBuffer vertex_buffer;
299 OGLBuffer uniform_buffer; 427 OGLBuffer uniform_buffer;
300 OGLFramebuffer framebuffer; 428 OGLFramebuffer framebuffer;
429
430 std::array<OGLTexture, 6> lighting_lut;
431 std::array<std::array<GLvec4, 256>, 6> lighting_lut_data;
301}; 432};
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 22022f7f4..ee4b54ab9 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -32,12 +32,10 @@ static void AppendSource(std::string& out, TevStageConfig::Source source,
32 out += "primary_color"; 32 out += "primary_color";
33 break; 33 break;
34 case Source::PrimaryFragmentColor: 34 case Source::PrimaryFragmentColor:
35 // HACK: Until we implement fragment lighting, use primary_color 35 out += "primary_fragment_color";
36 out += "primary_color";
37 break; 36 break;
38 case Source::SecondaryFragmentColor: 37 case Source::SecondaryFragmentColor:
39 // HACK: Until we implement fragment lighting, use zero 38 out += "secondary_fragment_color";
40 out += "vec4(0.0)";
41 break; 39 break;
42 case Source::Texture0: 40 case Source::Texture0:
43 out += "texture(tex[0], texcoord[0])"; 41 out += "texture(tex[0], texcoord[0])";
@@ -320,26 +318,229 @@ static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsi
320 out += "next_combiner_buffer.a = last_tex_env_out.a;\n"; 318 out += "next_combiner_buffer.a = last_tex_env_out.a;\n";
321} 319}
322 320
321/// Writes the code to emulate fragment lighting
322static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
323 // Define lighting globals
324 out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"
325 "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"
326 "vec3 light_vector = vec3(0.0);\n"
327 "vec3 refl_value = vec3(0.0);\n";
328
329 // Compute fragment normals
330 if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::NormalMap) {
331 // Bump mapping is enabled using a normal map, read perturbation vector from the selected texture
332 std::string bump_selector = std::to_string(config.lighting.bump_selector);
333 out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], texcoord[" + bump_selector + "]).rgb - 1.0;\n";
334
335 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher precision result
336 if (config.lighting.bump_renorm) {
337 std::string val = "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))";
338 out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n";
339 }
340 } else if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::TangentMap) {
341 // Bump mapping is enabled using a tangent map
342 LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)");
343 UNIMPLEMENTED();
344 } else {
345 // No bump mapping - surface local normal is just a unit normal
346 out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n";
347 }
348
349 // Rotate the surface-local normal by the interpolated normal quaternion to convert it to eyespace
350 out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n";
351
352 // Gets the index into the specified lookup table for specular lighting
353 auto GetLutIndex = [config](unsigned light_num, Regs::LightingLutInput input, bool abs) {
354 const std::string half_angle = "normalize(normalize(view) + light_vector)";
355 std::string index;
356 switch (input) {
357 case Regs::LightingLutInput::NH:
358 index = "dot(normal, " + half_angle + ")";
359 break;
360
361 case Regs::LightingLutInput::VH:
362 index = std::string("dot(normalize(view), " + half_angle + ")");
363 break;
364
365 case Regs::LightingLutInput::NV:
366 index = std::string("dot(normal, normalize(view))");
367 break;
368
369 case Regs::LightingLutInput::LN:
370 index = std::string("dot(light_vector, normal)");
371 break;
372
373 default:
374 LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input);
375 UNIMPLEMENTED();
376 break;
377 }
378
379 if (abs) {
380 // LUT index is in the range of (0.0, 1.0)
381 index = config.lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)";
382 return "(FLOAT_255 * clamp(" + index + ", 0.0, 1.0))";
383 } else {
384 // LUT index is in the range of (-1.0, 1.0)
385 index = "clamp(" + index + ", -1.0, 1.0)";
386 return "(FLOAT_255 * ((" + index + " < 0) ? " + index + " + 2.0 : " + index + ") / 2.0)";
387 }
388
389 return std::string();
390 };
391
392 // Gets the lighting lookup table value given the specified sampler and index
393 auto GetLutValue = [](Regs::LightingSampler sampler, std::string lut_index) {
394 return std::string("texture(lut[" + std::to_string((unsigned)sampler / 4) + "], " +
395 lut_index + ")[" + std::to_string((unsigned)sampler & 3) + "]");
396 };
397
398 // Write the code to emulate each enabled light
399 for (unsigned light_index = 0; light_index < config.lighting.src_num; ++light_index) {
400 const auto& light_config = config.lighting.light[light_index];
401 std::string light_src = "light_src[" + std::to_string(light_config.num) + "]";
402
403 // Compute light vector (directional or positional)
404 if (light_config.directional)
405 out += "light_vector = normalize(" + light_src + ".position);\n";
406 else
407 out += "light_vector = normalize(" + light_src + ".position + view);\n";
408
409 // Compute dot product of light_vector and normal, adjust if lighting is one-sided or two-sided
410 std::string dot_product = light_config.two_sided_diffuse ? "abs(dot(light_vector, normal))" : "max(dot(light_vector, normal), 0.0)";
411
412 // If enabled, compute distance attenuation value
413 std::string dist_atten = "1.0";
414 if (light_config.dist_atten_enable) {
415 std::string scale = std::to_string(light_config.dist_atten_scale);
416 std::string bias = std::to_string(light_config.dist_atten_bias);
417 std::string index = "(" + scale + " * length(-view - " + light_src + ".position) + " + bias + ")";
418 index = "((clamp(" + index + ", 0.0, FLOAT_255)))";
419 const unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + light_config.num);
420 dist_atten = GetLutValue((Regs::LightingSampler)lut_num, index);
421 }
422
423 // If enabled, clamp specular component if lighting result is negative
424 std::string clamp_highlights = config.lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0";
425
426 // Specular 0 component
427 std::string d0_lut_value = "1.0";
428 if (config.lighting.lut_d0.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution0)) {
429 // Lookup specular "distribution 0" LUT value
430 std::string index = GetLutIndex(light_config.num, config.lighting.lut_d0.type, config.lighting.lut_d0.abs_input);
431 d0_lut_value = "(" + std::to_string(config.lighting.lut_d0.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution0, index) + ")";
432 }
433 std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)";
434
435 // If enabled, lookup ReflectRed value, otherwise, 1.0 is used
436 if (config.lighting.lut_rr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectRed)) {
437 std::string index = GetLutIndex(light_config.num, config.lighting.lut_rr.type, config.lighting.lut_rr.abs_input);
438 std::string value = "(" + std::to_string(config.lighting.lut_rr.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectRed, index) + ")";
439 out += "refl_value.r = " + value + ";\n";
440 } else {
441 out += "refl_value.r = 1.0;\n";
442 }
443
444 // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used
445 if (config.lighting.lut_rg.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectGreen)) {
446 std::string index = GetLutIndex(light_config.num, config.lighting.lut_rg.type, config.lighting.lut_rg.abs_input);
447 std::string value = "(" + std::to_string(config.lighting.lut_rg.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectGreen, index) + ")";
448 out += "refl_value.g = " + value + ";\n";
449 } else {
450 out += "refl_value.g = refl_value.r;\n";
451 }
452
453 // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used
454 if (config.lighting.lut_rb.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectBlue)) {
455 std::string index = GetLutIndex(light_config.num, config.lighting.lut_rb.type, config.lighting.lut_rb.abs_input);
456 std::string value = "(" + std::to_string(config.lighting.lut_rb.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectBlue, index) + ")";
457 out += "refl_value.b = " + value + ";\n";
458 } else {
459 out += "refl_value.b = refl_value.r;\n";
460 }
461
462 // Specular 1 component
463 std::string d1_lut_value = "1.0";
464 if (config.lighting.lut_d1.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution1)) {
465 // Lookup specular "distribution 1" LUT value
466 std::string index = GetLutIndex(light_config.num, config.lighting.lut_d1.type, config.lighting.lut_d1.abs_input);
467 d1_lut_value = "(" + std::to_string(config.lighting.lut_d1.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution1, index) + ")";
468 }
469 std::string specular_1 = "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)";
470
471 // Fresnel
472 if (config.lighting.lut_fr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Fresnel)) {
473 // Lookup fresnel LUT value
474 std::string index = GetLutIndex(light_config.num, config.lighting.lut_fr.type, config.lighting.lut_fr.abs_input);
475 std::string value = "(" + std::to_string(config.lighting.lut_fr.scale) + " * " + GetLutValue(Regs::LightingSampler::Fresnel, index) + ")";
476
477 // Enabled for difffuse lighting alpha component
478 if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::PrimaryAlpha ||
479 config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
480 out += "diffuse_sum.a *= " + value + ";\n";
481
482 // Enabled for the specular lighting alpha component
483 if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::SecondaryAlpha ||
484 config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
485 out += "specular_sum.a *= " + value + ";\n";
486 }
487
488 // Compute primary fragment color (diffuse lighting) function
489 out += "diffuse_sum.rgb += ((" + light_src + ".diffuse * " + dot_product + ") + " + light_src + ".ambient) * " + dist_atten + ";\n";
490
491 // Compute secondary fragment color (specular lighting) function
492 out += "specular_sum.rgb += (" + specular_0 + " + " + specular_1 + ") * " + clamp_highlights + " * " + dist_atten + ";\n";
493 }
494
495 // Sum final lighting result
496 out += "diffuse_sum.rgb += lighting_global_ambient;\n";
497 out += "primary_fragment_color = clamp(diffuse_sum, vec4(0.0), vec4(1.0));\n";
498 out += "secondary_fragment_color = clamp(specular_sum, vec4(0.0), vec4(1.0));\n";
499}
500
323std::string GenerateFragmentShader(const PicaShaderConfig& config) { 501std::string GenerateFragmentShader(const PicaShaderConfig& config) {
324 std::string out = R"( 502 std::string out = R"(
325#version 330 core 503#version 330 core
326#define NUM_TEV_STAGES 6 504#define NUM_TEV_STAGES 6
505#define NUM_LIGHTS 8
506#define LIGHTING_LUT_SIZE 256
507#define FLOAT_255 (255.0 / 256.0)
327 508
328in vec4 primary_color; 509in vec4 primary_color;
329in vec2 texcoord[3]; 510in vec2 texcoord[3];
511in vec4 normquat;
512in vec3 view;
330 513
331out vec4 color; 514out vec4 color;
332 515
516struct LightSrc {
517 vec3 specular_0;
518 vec3 specular_1;
519 vec3 diffuse;
520 vec3 ambient;
521 vec3 position;
522};
523
333layout (std140) uniform shader_data { 524layout (std140) uniform shader_data {
334 vec4 const_color[NUM_TEV_STAGES]; 525 vec4 const_color[NUM_TEV_STAGES];
335 vec4 tev_combiner_buffer_color; 526 vec4 tev_combiner_buffer_color;
336 int alphatest_ref; 527 int alphatest_ref;
337 float depth_offset; 528 float depth_offset;
529 vec3 lighting_global_ambient;
530 LightSrc light_src[NUM_LIGHTS];
338}; 531};
339 532
340uniform sampler2D tex[3]; 533uniform sampler2D tex[3];
534uniform sampler1D lut[6];
535
536// Rotate the vector v by the quaternion q
537vec3 quaternion_rotate(vec4 q, vec3 v) {
538 return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
539}
341 540
342void main() { 541void main() {
542vec4 primary_fragment_color = vec4(0.0);
543vec4 secondary_fragment_color = vec4(0.0);
343)"; 544)";
344 545
345 // Do not do any sort of processing if it's obvious we're not going to pass the alpha test 546 // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
@@ -348,6 +549,9 @@ void main() {
348 return out; 549 return out;
349 } 550 }
350 551
552 if (config.lighting.enable)
553 WriteLighting(out, config);
554
351 out += "vec4 combiner_buffer = vec4(0.0);\n"; 555 out += "vec4 combiner_buffer = vec4(0.0);\n";
352 out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n"; 556 out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n";
353 out += "vec4 last_tex_env_out = vec4(0.0);\n"; 557 out += "vec4 last_tex_env_out = vec4(0.0);\n";
@@ -369,21 +573,28 @@ void main() {
369 573
370std::string GenerateVertexShader() { 574std::string GenerateVertexShader() {
371 std::string out = "#version 330 core\n"; 575 std::string out = "#version 330 core\n";
576
372 out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n"; 577 out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n";
373 out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n"; 578 out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n";
374 out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n"; 579 out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n";
375 out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n"; 580 out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n";
376 out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n"; 581 out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n";
582 out += "layout(location = " + std::to_string((int)ATTRIBUTE_NORMQUAT) + ") in vec4 vert_normquat;\n";
583 out += "layout(location = " + std::to_string((int)ATTRIBUTE_VIEW) + ") in vec3 vert_view;\n";
377 584
378 out += R"( 585 out += R"(
379out vec4 primary_color; 586out vec4 primary_color;
380out vec2 texcoord[3]; 587out vec2 texcoord[3];
588out vec4 normquat;
589out vec3 view;
381 590
382void main() { 591void main() {
383 primary_color = vert_color; 592 primary_color = vert_color;
384 texcoord[0] = vert_texcoord0; 593 texcoord[0] = vert_texcoord0;
385 texcoord[1] = vert_texcoord1; 594 texcoord[1] = vert_texcoord1;
386 texcoord[2] = vert_texcoord2; 595 texcoord[2] = vert_texcoord2;
596 normquat = vert_normquat;
597 view = vert_view;
387 gl_Position = vec4(vert_position.x, vert_position.y, -vert_position.z, vert_position.w); 598 gl_Position = vec4(vert_position.x, vert_position.y, -vert_position.z, vert_position.w);
388} 599}
389)"; 600)";
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 046aae14f..097242f6f 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -14,6 +14,8 @@ enum Attributes {
14 ATTRIBUTE_TEXCOORD0, 14 ATTRIBUTE_TEXCOORD0,
15 ATTRIBUTE_TEXCOORD1, 15 ATTRIBUTE_TEXCOORD1,
16 ATTRIBUTE_TEXCOORD2, 16 ATTRIBUTE_TEXCOORD2,
17 ATTRIBUTE_NORMQUAT,
18 ATTRIBUTE_VIEW,
17}; 19};
18 20
19/** 21/**
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index a82372995..ab4b6c7b1 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -170,6 +170,14 @@ void OpenGLState::Apply() {
170 } 170 }
171 } 171 }
172 172
173 // Lighting LUTs
174 for (unsigned i = 0; i < ARRAY_SIZE(lighting_lut); ++i) {
175 if (lighting_lut[i].texture_1d != cur_state.lighting_lut[i].texture_1d) {
176 glActiveTexture(GL_TEXTURE3 + i);
177 glBindTexture(GL_TEXTURE_1D, lighting_lut[i].texture_1d);
178 }
179 }
180
173 // Framebuffer 181 // Framebuffer
174 if (draw.framebuffer != cur_state.draw.framebuffer) { 182 if (draw.framebuffer != cur_state.draw.framebuffer) {
175 glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer); 183 glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer);
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index b8ab45bb8..e848058d7 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -62,6 +62,10 @@ public:
62 } texture_units[3]; 62 } texture_units[3];
63 63
64 struct { 64 struct {
65 GLuint texture_1d; // GL_TEXTURE_BINDING_1D
66 } lighting_lut[6];
67
68 struct {
65 GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING 69 GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
66 GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING 70 GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
67 GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING 71 GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
index 04c1d1a34..3d6c4e9e5 100644
--- a/src/video_core/renderer_opengl/pica_to_gl.h
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -10,6 +10,9 @@
10 10
11#include "video_core/pica.h" 11#include "video_core/pica.h"
12 12
13using GLvec3 = std::array<GLfloat, 3>;
14using GLvec4 = std::array<GLfloat, 4>;
15
13namespace PicaToGL { 16namespace PicaToGL {
14 17
15inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) { 18inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) {
@@ -175,7 +178,7 @@ inline GLenum StencilOp(Pica::Regs::StencilAction action) {
175 return stencil_op_table[(unsigned)action]; 178 return stencil_op_table[(unsigned)action];
176} 179}
177 180
178inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) { 181inline GLvec4 ColorRGBA8(const u32 color) {
179 return { { (color >> 0 & 0xFF) / 255.0f, 182 return { { (color >> 0 & 0xFF) / 255.0f,
180 (color >> 8 & 0xFF) / 255.0f, 183 (color >> 8 & 0xFF) / 255.0f,
181 (color >> 16 & 0xFF) / 255.0f, 184 (color >> 16 & 0xFF) / 255.0f,
@@ -183,4 +186,11 @@ inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) {
183 } }; 186 } };
184} 187}
185 188
189inline std::array<GLfloat, 3> LightColor(const Pica::Regs::LightColor& color) {
190 return { { color.r / 255.0f,
191 color.g / 255.0f,
192 color.b / 255.0f
193 } };
194}
195
186} // namespace 196} // namespace
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index a6a38f0af..ca3a6a6b4 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -81,8 +81,8 @@ struct ScreenRectVertex {
81 * The projection part of the matrix is trivial, hence these operations are represented 81 * The projection part of the matrix is trivial, hence these operations are represented
82 * by a 3x2 matrix. 82 * by a 3x2 matrix.
83 */ 83 */
84static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const float height) { 84static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, const float height) {
85 std::array<GLfloat, 3*2> matrix; 85 std::array<GLfloat, 3 * 2> matrix;
86 86
87 matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; 87 matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
88 matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; 88 matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;