commit
4195d9b3f8
@ -0,0 +1,388 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/pica.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
|
||||
using Pica::Regs;
|
||||
using TevStageConfig = Regs::TevStageConfig;
|
||||
|
||||
namespace GLShader {
|
||||
|
||||
/// Detects if a TEV stage is configured to be skipped (to avoid generating unnecessary code)
|
||||
static bool IsPassThroughTevStage(const TevStageConfig& stage) {
|
||||
return (stage.color_op == TevStageConfig::Operation::Replace &&
|
||||
stage.alpha_op == TevStageConfig::Operation::Replace &&
|
||||
stage.color_source1 == TevStageConfig::Source::Previous &&
|
||||
stage.alpha_source1 == TevStageConfig::Source::Previous &&
|
||||
stage.color_modifier1 == TevStageConfig::ColorModifier::SourceColor &&
|
||||
stage.alpha_modifier1 == TevStageConfig::AlphaModifier::SourceAlpha &&
|
||||
stage.GetColorMultiplier() == 1 &&
|
||||
stage.GetAlphaMultiplier() == 1);
|
||||
}
|
||||
|
||||
/// Writes the specified TEV stage source component(s)
|
||||
static void AppendSource(std::string& out, TevStageConfig::Source source,
|
||||
const std::string& index_name) {
|
||||
using Source = TevStageConfig::Source;
|
||||
switch (source) {
|
||||
case Source::PrimaryColor:
|
||||
out += "primary_color";
|
||||
break;
|
||||
case Source::PrimaryFragmentColor:
|
||||
// HACK: Until we implement fragment lighting, use primary_color
|
||||
out += "primary_color";
|
||||
break;
|
||||
case Source::SecondaryFragmentColor:
|
||||
// HACK: Until we implement fragment lighting, use zero
|
||||
out += "vec4(0.0)";
|
||||
break;
|
||||
case Source::Texture0:
|
||||
out += "texture(tex[0], texcoord[0])";
|
||||
break;
|
||||
case Source::Texture1:
|
||||
out += "texture(tex[1], texcoord[1])";
|
||||
break;
|
||||
case Source::Texture2:
|
||||
out += "texture(tex[2], texcoord[2])";
|
||||
break;
|
||||
case Source::PreviousBuffer:
|
||||
out += "combiner_buffer";
|
||||
break;
|
||||
case Source::Constant:
|
||||
((out += "const_color[") += index_name) += ']';
|
||||
break;
|
||||
case Source::Previous:
|
||||
out += "last_tex_env_out";
|
||||
break;
|
||||
default:
|
||||
out += "vec4(0.0)";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the color components to use for the specified TEV stage color modifier
|
||||
static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier modifier,
|
||||
TevStageConfig::Source source, const std::string& index_name) {
|
||||
using ColorModifier = TevStageConfig::ColorModifier;
|
||||
switch (modifier) {
|
||||
case ColorModifier::SourceColor:
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".rgb";
|
||||
break;
|
||||
case ColorModifier::OneMinusSourceColor:
|
||||
out += "vec3(1.0) - ";
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".rgb";
|
||||
break;
|
||||
case ColorModifier::SourceAlpha:
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".aaa";
|
||||
break;
|
||||
case ColorModifier::OneMinusSourceAlpha:
|
||||
out += "vec3(1.0) - ";
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".aaa";
|
||||
break;
|
||||
case ColorModifier::SourceRed:
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".rrr";
|
||||
break;
|
||||
case ColorModifier::OneMinusSourceRed:
|
||||
out += "vec3(1.0) - ";
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".rrr";
|
||||
break;
|
||||
case ColorModifier::SourceGreen:
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".ggg";
|
||||
break;
|
||||
case ColorModifier::OneMinusSourceGreen:
|
||||
out += "vec3(1.0) - ";
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".ggg";
|
||||
break;
|
||||
case ColorModifier::SourceBlue:
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".bbb";
|
||||
break;
|
||||
case ColorModifier::OneMinusSourceBlue:
|
||||
out += "vec3(1.0) - ";
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".bbb";
|
||||
break;
|
||||
default:
|
||||
out += "vec3(0.0)";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the alpha component to use for the specified TEV stage alpha modifier
|
||||
static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier modifier,
|
||||
TevStageConfig::Source source, const std::string& index_name) {
|
||||
using AlphaModifier = TevStageConfig::AlphaModifier;
|
||||
switch (modifier) {
|
||||
case AlphaModifier::SourceAlpha:
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".a";
|
||||
break;
|
||||
case AlphaModifier::OneMinusSourceAlpha:
|
||||
out += "1.0 - ";
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".a";
|
||||
break;
|
||||
case AlphaModifier::SourceRed:
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".r";
|
||||
break;
|
||||
case AlphaModifier::OneMinusSourceRed:
|
||||
out += "1.0 - ";
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".r";
|
||||
break;
|
||||
case AlphaModifier::SourceGreen:
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".g";
|
||||
break;
|
||||
case AlphaModifier::OneMinusSourceGreen:
|
||||
out += "1.0 - ";
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".g";
|
||||
break;
|
||||
case AlphaModifier::SourceBlue:
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".b";
|
||||
break;
|
||||
case AlphaModifier::OneMinusSourceBlue:
|
||||
out += "1.0 - ";
|
||||
AppendSource(out, source, index_name);
|
||||
out += ".b";
|
||||
break;
|
||||
default:
|
||||
out += "0.0";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the combiner function for the color components for the specified TEV stage operation
|
||||
static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation,
|
||||
const std::string& variable_name) {
|
||||
out += "clamp(";
|
||||
using Operation = TevStageConfig::Operation;
|
||||
switch (operation) {
|
||||
case Operation::Replace:
|
||||
out += variable_name + "[0]";
|
||||
break;
|
||||
case Operation::Modulate:
|
||||
out += variable_name + "[0] * " + variable_name + "[1]";
|
||||
break;
|
||||
case Operation::Add:
|
||||
out += variable_name + "[0] + " + variable_name + "[1]";
|
||||
break;
|
||||
case Operation::AddSigned:
|
||||
out += variable_name + "[0] + " + variable_name + "[1] - vec3(0.5)";
|
||||
break;
|
||||
case Operation::Lerp:
|
||||
// TODO(bunnei): Verify if HW actually does this per-component, otherwise we can just use builtin lerp
|
||||
out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])";
|
||||
break;
|
||||
case Operation::Subtract:
|
||||
out += variable_name + "[0] - " + variable_name + "[1]";
|
||||
break;
|
||||
case Operation::MultiplyThenAdd:
|
||||
out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]";
|
||||
break;
|
||||
case Operation::AddThenMultiply:
|
||||
out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]";
|
||||
break;
|
||||
default:
|
||||
out += "vec3(0.0)";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation);
|
||||
break;
|
||||
}
|
||||
out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0
|
||||
}
|
||||
|
||||
/// Writes the combiner function for the alpha component for the specified TEV stage operation
|
||||
static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation,
|
||||
const std::string& variable_name) {
|
||||
out += "clamp(";
|
||||
using Operation = TevStageConfig::Operation;
|
||||
switch (operation) {
|
||||
case Operation::Replace:
|
||||
out += variable_name + "[0]";
|
||||
break;
|
||||
case Operation::Modulate:
|
||||
out += variable_name + "[0] * " + variable_name + "[1]";
|
||||
break;
|
||||
case Operation::Add:
|
||||
out += variable_name + "[0] + " + variable_name + "[1]";
|
||||
break;
|
||||
case Operation::AddSigned:
|
||||
out += variable_name + "[0] + " + variable_name + "[1] - 0.5";
|
||||
break;
|
||||
case Operation::Lerp:
|
||||
out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])";
|
||||
break;
|
||||
case Operation::Subtract:
|
||||
out += variable_name + "[0] - " + variable_name + "[1]";
|
||||
break;
|
||||
case Operation::MultiplyThenAdd:
|
||||
out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]";
|
||||
break;
|
||||
case Operation::AddThenMultiply:
|
||||
out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]";
|
||||
break;
|
||||
default:
|
||||
out += "0.0";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation);
|
||||
break;
|
||||
}
|
||||
out += ", 0.0, 1.0)";
|
||||
}
|
||||
|
||||
/// Writes the if-statement condition used to evaluate alpha testing
|
||||
static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) {
|
||||
using CompareFunc = Regs::CompareFunc;
|
||||
switch (func) {
|
||||
case CompareFunc::Never:
|
||||
out += "true";
|
||||
break;
|
||||
case CompareFunc::Always:
|
||||
out += "false";
|
||||
break;
|
||||
case CompareFunc::Equal:
|
||||
case CompareFunc::NotEqual:
|
||||
case CompareFunc::LessThan:
|
||||
case CompareFunc::LessThanOrEqual:
|
||||
case CompareFunc::GreaterThan:
|
||||
case CompareFunc::GreaterThanOrEqual:
|
||||
{
|
||||
static const char* op[] = { "!=", "==", ">=", ">", "<=", "<", };
|
||||
unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal;
|
||||
out += "int(last_tex_env_out.a * 255.0f) " + std::string(op[index]) + " alphatest_ref";
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
out += "false";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the code to emulate the specified TEV stage
|
||||
static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) {
|
||||
auto& stage = config.tev_stages[index];
|
||||
if (!IsPassThroughTevStage(stage)) {
|
||||
std::string index_name = std::to_string(index);
|
||||
|
||||
out += "vec3 color_results_" + index_name + "[3] = vec3[3](";
|
||||
AppendColorModifier(out, stage.color_modifier1, stage.color_source1, index_name);
|
||||
out += ", ";
|
||||
AppendColorModifier(out, stage.color_modifier2, stage.color_source2, index_name);
|
||||
out += ", ";
|
||||
AppendColorModifier(out, stage.color_modifier3, stage.color_source3, index_name);
|
||||
out += ");\n";
|
||||
|
||||
out += "vec3 color_output_" + index_name + " = ";
|
||||
AppendColorCombiner(out, stage.color_op, "color_results_" + index_name);
|
||||
out += ";\n";
|
||||
|
||||
out += "float alpha_results_" + index_name + "[3] = float[3](";
|
||||
AppendAlphaModifier(out, stage.alpha_modifier1, stage.alpha_source1, index_name);
|
||||
out += ", ";
|
||||
AppendAlphaModifier(out, stage.alpha_modifier2, stage.alpha_source2, index_name);
|
||||
out += ", ";
|
||||
AppendAlphaModifier(out, stage.alpha_modifier3, stage.alpha_source3, index_name);
|
||||
out += ");\n";
|
||||
|
||||
out += "float alpha_output_" + index_name + " = ";
|
||||
AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name);
|
||||
out += ";\n";
|
||||
|
||||
out += "last_tex_env_out = vec4("
|
||||
"clamp(color_output_" + index_name + " * " + std::to_string(stage.GetColorMultiplier()) + ".0, vec3(0.0), vec3(1.0)),"
|
||||
"clamp(alpha_output_" + index_name + " * " + std::to_string(stage.GetAlphaMultiplier()) + ".0, 0.0, 1.0));\n";
|
||||
}
|
||||
|
||||
if (config.TevStageUpdatesCombinerBufferColor(index))
|
||||
out += "combiner_buffer.rgb = last_tex_env_out.rgb;\n";
|
||||
|
||||
if (config.TevStageUpdatesCombinerBufferAlpha(index))
|
||||
out += "combiner_buffer.a = last_tex_env_out.a;\n";
|
||||
}
|
||||
|
||||
std::string GenerateFragmentShader(const PicaShaderConfig& config) {
|
||||
std::string out = R"(
|
||||
#version 330
|
||||
#extension GL_ARB_explicit_uniform_location : require
|
||||
|
||||
#define NUM_TEV_STAGES 6
|
||||
|
||||
in vec4 primary_color;
|
||||
in vec2 texcoord[3];
|
||||
|
||||
out vec4 color;
|
||||
)";
|
||||
|
||||
using Uniform = RasterizerOpenGL::PicaShader::Uniform;
|
||||
out += "layout(location = " + std::to_string((int)Uniform::AlphaTestRef) + ") uniform int alphatest_ref;\n";
|
||||
out += "layout(location = " + std::to_string((int)Uniform::TevConstColors) + ") uniform vec4 const_color[NUM_TEV_STAGES];\n";
|
||||
out += "layout(location = " + std::to_string((int)Uniform::Texture0) + ") uniform sampler2D tex[3];\n";
|
||||
out += "layout(location = " + std::to_string((int)Uniform::TevCombinerBufferColor) + ") uniform vec4 tev_combiner_buffer_color;\n";
|
||||
|
||||
out += "void main() {\n";
|
||||
out += "vec4 combiner_buffer = tev_combiner_buffer_color;\n";
|
||||
out += "vec4 last_tex_env_out = vec4(0.0);\n";
|
||||
|
||||
// Do not do any sort of processing if it's obvious we're not going to pass the alpha test
|
||||
if (config.alpha_test_func == Regs::CompareFunc::Never) {
|
||||
out += "discard; }";
|
||||
return out;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < config.tev_stages.size(); ++index)
|
||||
WriteTevStage(out, config, (unsigned)index);
|
||||
|
||||
if (config.alpha_test_func != Regs::CompareFunc::Always) {
|
||||
out += "if (";
|
||||
AppendAlphaTestCondition(out, config.alpha_test_func);
|
||||
out += ") discard;\n";
|
||||
}
|
||||
|
||||
out += "color = last_tex_env_out;\n}";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string GenerateVertexShader() {
|
||||
std::string out = "#version 330\n";
|
||||
out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n";
|
||||
out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n";
|
||||
out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n";
|
||||
out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n";
|
||||
out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n";
|
||||
|
||||
out += R"(
|
||||
out vec4 primary_color;
|
||||
out vec2 texcoord[3];
|
||||
|
||||
void main() {
|
||||
primary_color = vert_color;
|
||||
texcoord[0] = vert_texcoord0;
|
||||
texcoord[1] = vert_texcoord1;
|
||||
texcoord[2] = vert_texcoord2;
|
||||
gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
|
||||
}
|
||||
)";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace GLShader
|
@ -0,0 +1,27 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
|
||||
namespace GLShader {
|
||||
|
||||
/**
|
||||
* Generates the GLSL vertex shader program source code for the current Pica state
|
||||
* @returns String of the shader source code
|
||||
*/
|
||||
std::string GenerateVertexShader();
|
||||
|
||||
/**
|
||||
* Generates the GLSL fragment shader program source code for the current Pica state
|
||||
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
|
||||
* configuration (NOTE: Use state in this struct only, not the Pica registers!)
|
||||
* @returns String of the shader source code
|
||||
*/
|
||||
std::string GenerateFragmentShader(const PicaShaderConfig& config);
|
||||
|
||||
} // namespace GLShader
|
@ -1,337 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace GLShaders {
|
||||
|
||||
const char g_vertex_shader[] = R"(
|
||||
#version 150 core
|
||||
|
||||
in vec2 vert_position;
|
||||
in vec2 vert_tex_coord;
|
||||
out vec2 frag_tex_coord;
|
||||
|
||||
// This is a truncated 3x3 matrix for 2D transformations:
|
||||
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
|
||||
// The third column performs translation.
|
||||
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
|
||||
// implicitly be [0, 0, 1]
|
||||
uniform mat3x2 modelview_matrix;
|
||||
|
||||
void main() {
|
||||
// Multiply input position by the rotscale part of the matrix and then manually translate by
|
||||
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
|
||||
// to `vec3(vert_position.xy, 1.0)`
|
||||
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
}
|
||||
)";
|
||||
|
||||
const char g_fragment_shader[] = R"(
|
||||
#version 150 core
|
||||
|
||||
in vec2 frag_tex_coord;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
}
|
||||
)";
|
||||
|
||||
const char g_vertex_shader_hw[] = R"(
|
||||
#version 150 core
|
||||
|
||||
#define NUM_VTX_ATTR 7
|
||||
|
||||
in vec4 vert_position;
|
||||
in vec4 vert_color;
|
||||
in vec2 vert_texcoords[3];
|
||||
|
||||
out vec4 o[NUM_VTX_ATTR];
|
||||
|
||||
void main() {
|
||||
o[2] = vert_color;
|
||||
o[3] = vec4(vert_texcoords[0].xy, vert_texcoords[1].xy);
|
||||
o[5] = vec4(0.0, 0.0, vert_texcoords[2].xy);
|
||||
|
||||
gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
|
||||
}
|
||||
)";
|
||||
|
||||
// TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms
|
||||
const char g_fragment_shader_hw[] = R"(
|
||||
#version 150 core
|
||||
|
||||
#define NUM_VTX_ATTR 7
|
||||
#define NUM_TEV_STAGES 6
|
||||
|
||||
#define SOURCE_PRIMARYCOLOR 0x0
|
||||
#define SOURCE_PRIMARYFRAGMENTCOLOR 0x1
|
||||
#define SOURCE_SECONDARYFRAGMENTCOLOR 0x2
|
||||
#define SOURCE_TEXTURE0 0x3
|
||||
#define SOURCE_TEXTURE1 0x4
|
||||
#define SOURCE_TEXTURE2 0x5
|
||||
#define SOURCE_TEXTURE3 0x6
|
||||
#define SOURCE_PREVIOUSBUFFER 0xd
|
||||
#define SOURCE_CONSTANT 0xe
|
||||
#define SOURCE_PREVIOUS 0xf
|
||||
|
||||
#define COLORMODIFIER_SOURCECOLOR 0x0
|
||||
#define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1
|
||||
#define COLORMODIFIER_SOURCEALPHA 0x2
|
||||
#define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3
|
||||
#define COLORMODIFIER_SOURCERED 0x4
|
||||
#define COLORMODIFIER_ONEMINUSSOURCERED 0x5
|
||||
#define COLORMODIFIER_SOURCEGREEN 0x8
|
||||
#define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9
|
||||
#define COLORMODIFIER_SOURCEBLUE 0xc
|
||||
#define COLORMODIFIER_ONEMINUSSOURCEBLUE 0xd
|
||||
|
||||
#define ALPHAMODIFIER_SOURCEALPHA 0x0
|
||||
#define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1
|
||||
#define ALPHAMODIFIER_SOURCERED 0x2
|
||||
#define ALPHAMODIFIER_ONEMINUSSOURCERED 0x3
|
||||
#define ALPHAMODIFIER_SOURCEGREEN 0x4
|
||||
#define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5
|
||||
#define ALPHAMODIFIER_SOURCEBLUE 0x6
|
||||
#define ALPHAMODIFIER_ONEMINUSSOURCEBLUE 0x7
|
||||
|
||||
#define OPERATION_REPLACE 0
|
||||
#define OPERATION_MODULATE 1
|
||||
#define OPERATION_ADD 2
|
||||
#define OPERATION_ADDSIGNED 3
|
||||
#define OPERATION_LERP 4
|
||||
#define OPERATION_SUBTRACT 5
|
||||
#define OPERATION_MULTIPLYTHENADD 8
|
||||
#define OPERATION_ADDTHENMULTIPLY 9
|
||||
|
||||
#define COMPAREFUNC_NEVER 0
|
||||
#define COMPAREFUNC_ALWAYS 1
|
||||
#define COMPAREFUNC_EQUAL 2
|
||||
#define COMPAREFUNC_NOTEQUAL 3
|
||||
#define COMPAREFUNC_LESSTHAN 4
|
||||
#define COMPAREFUNC_LESSTHANOREQUAL 5
|
||||
#define COMPAREFUNC_GREATERTHAN 6
|
||||
#define COMPAREFUNC_GREATERTHANOREQUAL 7
|
||||
|
||||
in vec4 o[NUM_VTX_ATTR];
|
||||
out vec4 color;
|
||||
|
||||
uniform bool alphatest_enabled;
|
||||
uniform int alphatest_func;
|
||||
uniform float alphatest_ref;
|
||||
|
||||
uniform sampler2D tex[3];
|
||||
|
||||
uniform vec4 tev_combiner_buffer_color;
|
||||
|
||||
struct TEVConfig
|
||||
{
|
||||
bool enabled;
|
||||
ivec3 color_sources;
|
||||
ivec3 alpha_sources;
|
||||
ivec3 color_modifiers;
|
||||
ivec3 alpha_modifiers;
|
||||
ivec2 color_alpha_op;
|
||||
ivec2 color_alpha_multiplier;
|
||||
vec4 const_color;
|
||||
bvec2 updates_combiner_buffer_color_alpha;
|
||||
};
|
||||
|
||||
uniform TEVConfig tev_cfgs[NUM_TEV_STAGES];
|
||||
|
||||
vec4 g_combiner_buffer;
|
||||
vec4 g_last_tex_env_out;
|
||||
vec4 g_const_color;
|
||||
|
||||
vec4 GetSource(int source) {
|
||||
if (source == SOURCE_PRIMARYCOLOR) {
|
||||
return o[2];
|
||||
} else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) {
|
||||
// HACK: Until we implement fragment lighting, use primary_color
|
||||
return o[2];
|
||||
} else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) {
|
||||
// HACK: Until we implement fragment lighting, use zero
|
||||
return vec4(0.0, 0.0, 0.0, 0.0);
|
||||
} else if (source == SOURCE_TEXTURE0) {
|
||||
return texture(tex[0], o[3].xy);
|
||||
} else if (source == SOURCE_TEXTURE1) {
|
||||
return texture(tex[1], o[3].zw);
|
||||
} else if (source == SOURCE_TEXTURE2) {
|
||||
// TODO: Unverified
|
||||
return texture(tex[2], o[5].zw);
|
||||
} else if (source == SOURCE_TEXTURE3) {
|
||||
// TODO: no 4th texture?
|
||||
} else if (source == SOURCE_PREVIOUSBUFFER) {
|
||||
return g_combiner_buffer;
|
||||
} else if (source == SOURCE_CONSTANT) {
|
||||
return g_const_color;
|
||||
} else if (source == SOURCE_PREVIOUS) {
|
||||
return g_last_tex_env_out;
|
||||
}
|
||||
|
||||
return vec4(0.0);
|
||||
}
|
||||
|
||||
vec3 GetColorModifier(int factor, vec4 color) {
|
||||
if (factor == COLORMODIFIER_SOURCECOLOR) {
|
||||
return color.rgb;
|
||||
} else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) {
|
||||
return vec3(1.0) - color.rgb;
|
||||
} else if (factor == COLORMODIFIER_SOURCEALPHA) {
|
||||
return color.aaa;
|
||||
} else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) {
|
||||
return vec3(1.0) - color.aaa;
|
||||
} else if (factor == COLORMODIFIER_SOURCERED) {
|
||||
return color.rrr;
|
||||
} else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) {
|
||||
return vec3(1.0) - color.rrr;
|
||||
} else if (factor == COLORMODIFIER_SOURCEGREEN) {
|
||||
return color.ggg;
|
||||
} else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) {
|
||||
return vec3(1.0) - color.ggg;
|
||||
} else if (factor == COLORMODIFIER_SOURCEBLUE) {
|
||||
return color.bbb;
|
||||
} else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) {
|
||||
return vec3(1.0) - color.bbb;
|
||||
}
|
||||
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
float GetAlphaModifier(int factor, vec4 color) {
|
||||
if (factor == ALPHAMODIFIER_SOURCEALPHA) {
|
||||
return color.a;
|
||||
} else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) {
|
||||
return 1.0 - color.a;
|
||||
} else if (factor == ALPHAMODIFIER_SOURCERED) {
|
||||
return color.r;
|
||||
} else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) {
|
||||
return 1.0 - color.r;
|
||||
} else if (factor == ALPHAMODIFIER_SOURCEGREEN) {
|
||||
return color.g;
|
||||
} else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) {
|
||||
return 1.0 - color.g;
|
||||
} else if (factor == ALPHAMODIFIER_SOURCEBLUE) {
|
||||
return color.b;
|
||||
} else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) {
|
||||
return 1.0 - color.b;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
vec3 ColorCombine(int op, vec3 color[3]) {
|
||||
if (op == OPERATION_REPLACE) {
|
||||
return color[0];
|
||||
} else if (op == OPERATION_MODULATE) {
|
||||
return color[0] * color[1];
|
||||
} else if (op == OPERATION_ADD) {
|
||||
return min(color[0] + color[1], 1.0);
|
||||
} else if (op == OPERATION_ADDSIGNED) {
|
||||
return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0);
|
||||
} else if (op == OPERATION_LERP) {
|
||||
return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]);
|
||||
} else if (op == OPERATION_SUBTRACT) {
|
||||
return max(color[0] - color[1], 0.0);
|
||||
} else if (op == OPERATION_MULTIPLYTHENADD) {
|
||||
return min(color[0] * color[1] + color[2], 1.0);
|
||||
} else if (op == OPERATION_ADDTHENMULTIPLY) {
|
||||
return min(color[0] + color[1], 1.0) * color[2];
|
||||
}
|
||||
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
float AlphaCombine(int op, float alpha[3]) {
|
||||
if (op == OPERATION_REPLACE) {
|
||||
return alpha[0];
|
||||
} else if (op == OPERATION_MODULATE) {
|
||||
return alpha[0] * alpha[1];
|
||||
} else if (op == OPERATION_ADD) {
|
||||
return min(alpha[0] + alpha[1], 1.0);
|
||||
} else if (op == OPERATION_ADDSIGNED) {
|
||||
return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0);
|
||||
} else if (op == OPERATION_LERP) {
|
||||
return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]);
|
||||
} else if (op == OPERATION_SUBTRACT) {
|
||||
return max(alpha[0] - alpha[1], 0.0);
|
||||
} else if (op == OPERATION_MULTIPLYTHENADD) {
|
||||
return min(alpha[0] * alpha[1] + alpha[2], 1.0);
|
||||
} else if (op == OPERATION_ADDTHENMULTIPLY) {
|
||||
return min(alpha[0] + alpha[1], 1.0) * alpha[2];
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
g_combiner_buffer = tev_combiner_buffer_color;
|
||||
|
||||
for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) {
|
||||
if (tev_cfgs[tex_env_idx].enabled) {
|
||||
g_const_color = tev_cfgs[tex_env_idx].const_color;
|
||||
|
||||
vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)),
|
||||
GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)),
|
||||
GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z)));
|
||||
vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results);
|
||||
|
||||
float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)),
|
||||
GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)),
|
||||
GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z)));
|
||||
float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results);
|
||||
|
||||
g_last_tex_env_out = vec4(min(color_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.x, 1.0), min(alpha_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.y, 1.0));
|
||||
}
|
||||
|
||||
if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) {
|
||||
g_combiner_buffer.rgb = g_last_tex_env_out.rgb;
|
||||
}
|
||||
|
||||
if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) {
|
||||
g_combiner_buffer.a = g_last_tex_env_out.a;
|
||||
}
|
||||
}
|
||||
|
||||
if (alphatest_enabled) {
|
||||
if (alphatest_func == COMPAREFUNC_NEVER) {
|
||||
discard;
|
||||
} else if (alphatest_func == COMPAREFUNC_ALWAYS) {
|
||||
|
||||
} else if (alphatest_func == COMPAREFUNC_EQUAL) {
|
||||
if (g_last_tex_env_out.a != alphatest_ref) {
|
||||
discard;
|
||||
}
|
||||
} else if (alphatest_func == COMPAREFUNC_NOTEQUAL) {
|
||||
if (g_last_tex_env_out.a == alphatest_ref) {
|
||||
discard;
|
||||
}
|
||||
} else if (alphatest_func == COMPAREFUNC_LESSTHAN) {
|
||||
if (g_last_tex_env_out.a >= alphatest_ref) {
|
||||
discard;
|
||||
}
|
||||
} else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) {
|
||||
if (g_last_tex_env_out.a > alphatest_ref) {
|
||||
discard;
|
||||
}
|
||||
} else if (alphatest_func == COMPAREFUNC_GREATERTHAN) {
|
||||
if (g_last_tex_env_out.a <= alphatest_ref) {
|
||||
discard;
|
||||
}
|
||||
} else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) {
|
||||
if (g_last_tex_env_out.a < alphatest_ref) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color = g_last_tex_env_out;
|
||||
}
|
||||
)";
|
||||
|
||||
}
|
Loading…
Reference in New Issue