mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
d215d8a91e
Gamma-correct blending and shader math can be enabled globally via the new 't.gammacorrect' boolean flag in love.conf. The new function love.graphics.isGammaCorrect will return true if it was requested in love.conf and is supported on the system. When gamma correct rendering is enabled, colors (including the colors of pixels from images) are automatically converted from sRGB to linear RGB before use. When drawing to the main screen or to a canvas with the 'normal' or 'srgb' format, the final output colors of pixel shaders are automatically converted from linear RGB to sRGB after blending and before the color is stored in the pixel. This lets the rendering pipeline do math using linear RGB values for colors rather than sRGB values, so the math is correct, without making users of the APIs manually linearize their colors with the love.math.gammaToLinear function (which still exists). The final output of the screen is encoded as sRGB, which is what systems expect. Canvases (except when otherwise requested) store their contents with sRGB encoding for increased precision with darker colors. the 'srgb' window setting flag has been removed, as well as the 'srgb' image flag. A new image flag 'linear' has been added, which when set to true will cause the colors of the image to always be treated as linear RGB rather than sRGB, when gamma-correct rendering is enabled. A new function 'Shader:sendColor' has been added, which has the same argument structure as 'Shader:send' but expects colors in the range of [0, 255]. When gamma-correct rendering is enabled it automatically gamma-corrects the given colors (by applying gammaToLinear to them.) New shader code functions have been added: gammaToLinear, linearToGamma, gammaCorrectColor, and unGammaCorrectColor. When gamma-correct rendering is enabled, the LOVE_GAMMA_CORRECT #define is set and gammaCorrectColor and unGammaCorrectColor are aliases for gammaToLinear and linearToGamma respectively, otherwise the functions do nothing. The new shader functions have 'precise' and 'fast' variants. If the LOVE_PRECISE_GAMMA define is set, then the normal functions default to the precise variants, otherwise they default to the fast variants. Currently the define is set in vertex shaders and not set in pixel shaders. The default per-vertex color is automatically gamma-corrected by LÖVE when gamma correct rendering is enabled, but any custom named per-vertex attribute specified with the new custom attribute Mesh functionality won't be, unless 'gammaCorrectColor' or similar functions are explicitly used (preferably inside the vertex shader code that has the custom attribute.)
330 lines
10 KiB
Lua
330 lines
10 KiB
Lua
R"luastring"--(
|
|
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
|
|
-- There is a matching delimiter at the bottom of the file.
|
|
|
|
--[[
|
|
Copyright (c) 2006-2015 LOVE Development Team
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
--]]
|
|
|
|
local table_concat = table.concat
|
|
|
|
-- SHADERS
|
|
|
|
local GLSL = {}
|
|
|
|
GLSL.VERSION = "#version 120"
|
|
GLSL.VERSION_ES = "#version 100"
|
|
|
|
GLSL.SYNTAX = [[
|
|
#ifndef GL_ES
|
|
#define lowp
|
|
#define mediump
|
|
#define highp
|
|
#endif
|
|
#define number float
|
|
#define Image sampler2D
|
|
#define extern uniform
|
|
#define Texel texture2D
|
|
#pragma optionNV(strict on)]]
|
|
|
|
-- Uniforms shared by the vertex and pixel shader stages.
|
|
GLSL.UNIFORMS = [[
|
|
#ifdef GL_ES
|
|
// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
|
|
// but we can't guarantee that highp is always supported in fragment shaders...
|
|
// We *really* don't want to use mediump for these in vertex shaders though.
|
|
#if defined(VERTEX) || defined(GL_FRAGMENT_PRECISION_HIGH)
|
|
#define LOVE_UNIFORM_PRECISION highp
|
|
#else
|
|
#define LOVE_UNIFORM_PRECISION mediump
|
|
#endif
|
|
uniform LOVE_UNIFORM_PRECISION mat4 TransformMatrix;
|
|
uniform LOVE_UNIFORM_PRECISION mat4 ProjectionMatrix;
|
|
uniform LOVE_UNIFORM_PRECISION mat4 TransformProjectionMatrix;
|
|
uniform LOVE_UNIFORM_PRECISION mat3 NormalMatrix;
|
|
#else
|
|
#define TransformMatrix gl_ModelViewMatrix
|
|
#define ProjectionMatrix gl_ProjectionMatrix
|
|
#define TransformProjectionMatrix gl_ModelViewProjectionMatrix
|
|
#define NormalMatrix gl_NormalMatrix
|
|
#endif
|
|
uniform mediump vec4 love_ScreenSize;]]
|
|
|
|
GLSL.FUNCTIONS = [[
|
|
float gammaToLinearPrecise(float c) {
|
|
return c <= 0.04045 ? c * 0.077399380804954 : pow((c + 0.055) * 0.9478672985782, 2.4);
|
|
}
|
|
vec3 gammaToLinearPrecise(vec3 c) {
|
|
bvec3 leq = lessThanEqual(c, vec3(0.04045));
|
|
c.r = leq.r ? c.r * 0.077399380804954 : pow((c.r + 0.055) * 0.9478672985782, 2.4);
|
|
c.g = leq.g ? c.g * 0.077399380804954 : pow((c.g + 0.055) * 0.9478672985782, 2.4);
|
|
c.b = leq.b ? c.b * 0.077399380804954 : pow((c.b + 0.055) * 0.9478672985782, 2.4);
|
|
return c;
|
|
}
|
|
vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); }
|
|
float linearToGammaPrecise(float c) {
|
|
return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;
|
|
}
|
|
vec3 linearToGammaPrecise(vec3 c) {
|
|
bvec3 lt = lessThan(c, vec3(0.0031308));
|
|
c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055;
|
|
c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055;
|
|
c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055;
|
|
return c;
|
|
}
|
|
vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); }
|
|
|
|
// pow(x, 2.2) isn't an amazing approximation, but at least it's efficient...
|
|
mediump float gammaToLinearFast(mediump float c) { return pow(max(c, 0.0), 2.2); }
|
|
mediump vec3 gammaToLinearFast(mediump vec3 c) { return pow(max(c, vec3(0.0)), vec3(2.2)); }
|
|
mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); }
|
|
mediump float linearToGammaFast(mediump float c) { return pow(max(c, 0.0), 1.0 / 2.2); }
|
|
mediump vec3 linearToGammaFast(mediump vec3 c) { return pow(max(c, vec3(0.0)), vec3(1.0 / 2.2)); }
|
|
mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); }
|
|
|
|
#ifdef LOVE_PRECISE_GAMMA
|
|
#define gammaToLinear gammaToLinearPrecise
|
|
#define linearToGamma linearToGammaPrecise
|
|
#else
|
|
#define gammaToLinear gammaToLinearFast
|
|
#define linearToGamma linearToGammaFast
|
|
#endif
|
|
|
|
#ifdef LOVE_GAMMA_CORRECT
|
|
#define gammaCorrectColor gammaToLinear
|
|
#define unGammaCorrectColor linearToGamma
|
|
#define gammaCorrectColorPrecise gammaToLinearPrecise
|
|
#define unGammaCorrectColorPrecise linearToGammaPrecise
|
|
#define gammaCorrectColorFast gammaToLinearFast
|
|
#define unGammaCorrectColorFast linearToGammaFast
|
|
#else
|
|
#define gammaCorrectColor
|
|
#define unGammaCorrectColor
|
|
#define gammaCorrectColorPrecise
|
|
#define unGammaCorrectColorPrecise
|
|
#define gammaCorrectColorFast
|
|
#define unGammaCorrectColorFast
|
|
#endif]]
|
|
|
|
GLSL.VERTEX = {
|
|
HEADER = [[
|
|
#define VERTEX
|
|
#define LOVE_PRECISE_GAMMA
|
|
|
|
attribute vec4 VertexPosition;
|
|
attribute vec4 VertexTexCoord;
|
|
attribute vec4 VertexColor;
|
|
attribute vec4 ConstantColor;
|
|
|
|
varying vec4 VaryingTexCoord;
|
|
varying vec4 VaryingColor;
|
|
|
|
#ifdef GL_ES
|
|
uniform mediump float love_PointSize;
|
|
#endif]],
|
|
|
|
FOOTER = [[
|
|
void main() {
|
|
VaryingTexCoord = VertexTexCoord;
|
|
VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor;
|
|
#ifdef GL_ES
|
|
gl_PointSize = love_PointSize;
|
|
#endif
|
|
gl_Position = position(TransformProjectionMatrix, VertexPosition);
|
|
}]],
|
|
}
|
|
|
|
GLSL.PIXEL = {
|
|
HEADER = [[
|
|
#define PIXEL
|
|
|
|
#ifdef GL_ES
|
|
precision mediump float;
|
|
#endif
|
|
|
|
varying mediump vec4 VaryingTexCoord;
|
|
varying lowp vec4 VaryingColor;
|
|
|
|
#define love_Canvases gl_FragData
|
|
|
|
uniform sampler2D _tex0_;]],
|
|
|
|
FOOTER = [[
|
|
void main() {
|
|
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
|
float dummy = Texel(_tex0_, vec2(.5)).r;
|
|
|
|
// See Shader::checkSetScreenParams in Shader.cpp.
|
|
vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w);
|
|
|
|
gl_FragColor = effect(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord);
|
|
}]],
|
|
|
|
FOOTER_MULTI_CANVAS = [[
|
|
void main() {
|
|
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
|
float dummy = Texel(_tex0_, vec2(.5)).r;
|
|
|
|
// See Shader::checkSetScreenParams in Shader.cpp.
|
|
vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w);
|
|
|
|
effects(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord);
|
|
}]],
|
|
}
|
|
|
|
local function createVertexCode(vertexcode, lang)
|
|
local vertexcodes = {
|
|
lang == "glsles" and GLSL.VERSION_ES or GLSL.VERSION,
|
|
GLSL.SYNTAX,
|
|
love.graphics.isGammaCorrect() and "#define LOVE_GAMMA_CORRECT 1" or "",
|
|
GLSL.VERTEX.HEADER, GLSL.UNIFORMS,
|
|
GLSL.FUNCTIONS,
|
|
lang == "glsles" and "#line 1" or "#line 0",
|
|
vertexcode,
|
|
GLSL.VERTEX.FOOTER,
|
|
}
|
|
return table_concat(vertexcodes, "\n")
|
|
end
|
|
|
|
local function createPixelCode(pixelcode, is_multicanvas, lang)
|
|
local pixelcodes = {
|
|
lang == "glsles" and GLSL.VERSION_ES or GLSL.VERSION,
|
|
GLSL.SYNTAX,
|
|
love.graphics.isGammaCorrect() and "#define LOVE_GAMMA_CORRECT 1" or "",
|
|
GLSL.PIXEL.HEADER, GLSL.UNIFORMS,
|
|
GLSL.FUNCTIONS,
|
|
lang == "glsles" and "#line 1" or "#line 0",
|
|
pixelcode,
|
|
is_multicanvas and GLSL.PIXEL.FOOTER_MULTI_CANVAS or GLSL.PIXEL.FOOTER,
|
|
}
|
|
return table_concat(pixelcodes, "\n")
|
|
end
|
|
|
|
local function isVertexCode(code)
|
|
return code:match("vec4%s+position%s*%(") ~= nil
|
|
end
|
|
|
|
local function isPixelCode(code)
|
|
if code:match("vec4%s+effect%s*%(") then
|
|
return true
|
|
elseif code:match("void%s+effects%s*%(") then
|
|
-- function for rendering to multiple canvases simultaneously
|
|
return true, true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function love.graphics._shaderCodeToGLSL(arg1, arg2)
|
|
local vertexcode, pixelcode
|
|
local is_multicanvas = false -- whether pixel code has "effects" function instead of "effect"
|
|
|
|
local lang = "glsl"
|
|
if (love.graphics.getRendererInfo()) == "OpenGL ES" then
|
|
lang = "glsles"
|
|
end
|
|
|
|
if arg1 then
|
|
if isVertexCode(arg1) then
|
|
vertexcode = arg1 -- first arg contains vertex shader code
|
|
end
|
|
|
|
local ispixel, isMultiCanvas = isPixelCode(arg1)
|
|
if ispixel then
|
|
pixelcode = arg1 -- first arg contains pixel shader code
|
|
is_multicanvas = isMultiCanvas
|
|
end
|
|
end
|
|
|
|
if arg2 then
|
|
if isVertexCode(arg2) then
|
|
vertexcode = arg2 -- second arg contains vertex shader code
|
|
end
|
|
|
|
local ispixel, isMultiCanvas = isPixelCode(arg2)
|
|
if ispixel then
|
|
pixelcode = arg2 -- second arg contains pixel shader code
|
|
is_multicanvas = isMultiCanvas
|
|
end
|
|
end
|
|
|
|
if vertexcode then
|
|
vertexcode = createVertexCode(vertexcode, lang)
|
|
end
|
|
if pixelcode then
|
|
pixelcode = createPixelCode(pixelcode, is_multicanvas, lang)
|
|
end
|
|
|
|
return vertexcode, pixelcode
|
|
end
|
|
|
|
function love.graphics._transformGLSLErrorMessages(message)
|
|
local shadertype = message:match("Cannot compile (%a+) shader code")
|
|
if not shadertype then return message end
|
|
local lines = {"Cannot compile "..shadertype.." shader code:"}
|
|
for l in message:gmatch("[^\n]+") do
|
|
-- nvidia compiler message:
|
|
-- 0(<linenumber>) : error/warning [NUMBER]: <error message>
|
|
local linenumber, what, message = l:match("^0%((%d+)%)%s*:%s*(%w+)[^:]+:%s*(.+)$")
|
|
if not linenumber then
|
|
-- ati compiler message:
|
|
-- ERROR 0:<linenumber>: error/warning(#[NUMBER]) [ERRORNAME]: <errormessage>
|
|
linenumber, what, message = l:match("^%w+: 0:(%d+):%s*(%w+)%([^%)]+%)%s*(.+)$")
|
|
if not linenumber then
|
|
-- OSX compiler message (?):
|
|
-- ERROR: 0:<linenumber>: <errormessage>
|
|
what, linenumber, message = l:match("^(%w+): %d+:(%d+): (.+)$")
|
|
end
|
|
end
|
|
if linenumber and what and message then
|
|
lines[#lines+1] = ("Line %d: %s: %s"):format(linenumber, what, message)
|
|
end
|
|
end
|
|
-- did not match any known error messages
|
|
if #lines == 1 then return message end
|
|
return table_concat(lines, "\n")
|
|
end
|
|
|
|
local defaultcode = {
|
|
vertex = [[
|
|
vec4 position(mat4 transform_proj, vec4 vertpos) {
|
|
return transform_proj * vertpos;
|
|
}]],
|
|
pixel = [[
|
|
vec4 effect(lowp vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
|
return Texel(tex, texcoord) * vcolor;
|
|
}]]
|
|
}
|
|
|
|
local defaults = {
|
|
opengl = {
|
|
createVertexCode(defaultcode.vertex, "glsl"),
|
|
createPixelCode(defaultcode.pixel, false, "glsl"),
|
|
},
|
|
opengles = {
|
|
createVertexCode(defaultcode.vertex, "glsles"),
|
|
createPixelCode(defaultcode.pixel, false, "glsles"),
|
|
},
|
|
}
|
|
|
|
love.graphics._setDefaultShaderCode(defaults)
|
|
|
|
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
|
--)luastring"--"
|