scummvm/engines/twp/shaders.cpp
Eugene Sandulenko ecbec954d9
Revert "TWP: Pull the engine by request"
This reverts commit 16e2606853.
2024-03-16 22:12:44 +01:00

408 lines
12 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "twp/twp.h"
#include "twp/shaders.h"
namespace Twp {
const char *vsrc = R"(
uniform mat4 u_transform;
attribute vec2 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoords;
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
gl_Position = u_transform * vec4(a_position, 0.0, 1.0);
v_color = a_color;
v_texCoords = a_texCoords;
})";
const char* bwShader = R"(
varying vec2 v_texCoords;
varying vec4 v_color;
uniform sampler2D u_texture;
void main() {
vec4 texColor = texture2D(u_texture, v_texCoords);
vec4 col = v_color * texColor;
float gray = dot(col.xyz, vec3(0.299, 0.587, 0.114));
gl_FragColor = vec4(gray, gray, gray, col.a);
})";
const char* ghostShader = R"(
// Work in progress ghost shader.. Too over the top at the moment, it'll make you sick.
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform float iGlobalTime;
uniform float iFade;
uniform float wobbleIntensity;
uniform vec3 shadows;
uniform vec3 midtones;
uniform vec3 highlights;
const float speed = 0.1;
const float emboss = 0.70;
const float intensity = 0.6;
const int steps = 4;
const float frequency = 9.0;
float colour(vec2 coord) {
float col = 0.0;
float timeSpeed = iGlobalTime * speed;
vec2 adjc = coord;
adjc.x += timeSpeed; // adjc0.x += fcos*timeSpeed;
float sum0 = cos(adjc.x * frequency) * intensity;
col += sum0;
adjc = coord;
float fcos = 0.623489797;
float fsin = 0.781831503;
adjc.x += fcos * timeSpeed;
adjc.y -= fsin * timeSpeed;
float sum1 = cos((adjc.x * fcos - adjc.y * fsin) * frequency) * intensity;
col += sum1;
adjc = coord;
fcos = -0.900968909;
fsin = 0.433883607;
adjc.x += fcos * timeSpeed;
adjc.y -= fsin * timeSpeed;
col += cos((adjc.x * fcos - adjc.y * fsin) * frequency) * intensity;
// do same in reverse.
col += sum1;
col += sum0;
return cos(col);
}
vec3 rgb2hsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
float rand(vec2 Input) {
float dt = dot(Input, vec2(12.9898, 78.233));
float sn = mod(dt, 3.14);
return fract(sin(sn) * 43758.5453123);
}
float color_balance(float col, float l, vec3 change) {
// NOTE: change = (shadow, midtones, highlights)
float sup = 83.0; // shadow upper bounds
float mup = 166.0; // midtones upper bounds
float value = col * 255.0;
l = l * 100.0;
if (l < sup) {
// shadow
float f = (sup - l + 1.0) / (sup + 1.0);
value += change.x * f;
} else if (l < mup) {
// midtone
float mrange = (mup - sup) / 2.0;
float mid = mrange + sup;
float diff = mid - l;
diff = -diff;
if (diff < 0.0) {
float f = 1.0 - (diff + 1.0) / (mrange + 1.0);
value += change.y * f;
}
} else {
// highlight
float f = (l - mup + 1.0) / (255.0 - mup + 1.0);
value += change.z * f;
}
value = min(255.0, max(0.0, value));
return value / 255.0;
}
vec2 rgb2cv(vec3 RGB) {
vec4 P = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0 / 3.0) : vec4(RGB.gb, 0.0, -1.0 / 3.0);
vec4 Q = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
float C = Q.x - min(Q.w, Q.y);
return vec2(C, Q.x);
}
float rgbToLuminance(vec3 RGB) {
float cMax = max(max(RGB.x, RGB.y), RGB.z);
float cMin = min(min(RGB.x, RGB.y), RGB.z);
return (cMax + cMin) * 0.5;
}
void main(void) {
vec2 c1 = v_texCoords;
float cc1 = colour(c1);
vec2 offset;
c1.x += (0.001 * wobbleIntensity); // appx 12 pixels horizontal
offset.x = emboss * (cc1 - colour(c1));
c1.x = v_texCoords.x;
c1.y += (0.002 * wobbleIntensity); // appx 12 pixels verticle
offset.y = emboss * (cc1 - colour(c1));
// TODO: The effect should be centered around Franklyns position in the room, not the center
// if ( emitFromCenter == 1)
{
vec2 center = vec2(0.5, 0.5);
float distToCenter = distance(center, v_texCoords);
offset *= distToCenter * 2.0;
}
c1 = v_texCoords;
c1 += (offset * iFade);
vec3 col = vec3(0, 0, 0);
if (c1.x >= 0.0 && c1.x < (1.0 - 0.003125)) {
col = texture2D(u_texture, c1).rgb;
float intensity = rgbToLuminance(col); //(col.r + col.g + col.b) * 0.333333333;
// Exponential Shadows
float shadowsBleed = 1.0 - intensity;
shadowsBleed *= shadowsBleed;
shadowsBleed *= shadowsBleed;
// Exponential midtones
float midtonesBleed = 1.0 - abs(-1.0 + intensity * 2.0);
midtonesBleed *= midtonesBleed;
midtonesBleed *= midtonesBleed;
// Exponential Hilights
float hilightsBleed = intensity;
hilightsBleed *= hilightsBleed;
hilightsBleed *= hilightsBleed;
vec3 colorization = col.rgb + shadows * shadowsBleed +
midtones * midtonesBleed +
highlights * hilightsBleed;
colorization = mix(col, colorization, iFade);
// col = lerp(col, colorization, _Amount);
col = min(vec3(1.0), max(vec3(0.0), colorization));
}
gl_FragColor = v_color * vec4(col, texture2D(u_texture, c1).a);
})";
const char* sepiaShader = R"(
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform float sepiaFlicker;
uniform float RandomValue[5];
uniform float TimeLapse;
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x * 34.0) + 1.0) * x); }
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
m = m * m;
m = m * m;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
void main(void) {
const float RADIUS = 0.75;
const float SOFTNESS = 0.45;
const float ScratchValue = 0.3;
vec4 texColor = texture2D(u_texture, v_texCoords);
vec4 col = v_color * texColor;
float gray = dot(col.rgb, vec3(0.299, 0.587, 0.114));
vec2 dist = vec2(v_texCoords.x - 0.5, v_texCoords.y - 0.5);
vec3 sepiaColor = vec3(gray) * vec3(0.9, 0.8, 0.6); // vec3(1.2, 1.0, 0.8);
float len = dot(dist, dist);
float vignette = smoothstep(RADIUS, RADIUS - SOFTNESS, len);
// float vignette = (1.0 - len);
col.rgb = mix(col.rgb, sepiaColor, 0.80) * vignette * sepiaFlicker; // Want to keep SOME of the original color, so only use 80% sepia
// col.rgb = vec3( vignette ) * sepiaFlicker;
for (int i = 0; i < 1; i++) {
if (RandomValue[i] < ScratchValue) {
// Pick a random spot to show scratches
float dist = 1.0 / ScratchValue;
float d = distance(v_texCoords, vec2(RandomValue[i] * dist, RandomValue[i] * dist));
if (d < 0.4) {
// Generate the scratch
float xPeriod = 8.0;
float yPeriod = 1.0;
float pi = 3.141592;
float phase = TimeLapse;
float turbulence = snoise(v_texCoords * 2.5);
float vScratch = 0.5 + (sin(((v_texCoords.x * xPeriod + v_texCoords.y * yPeriod + turbulence)) * pi + phase) * 0.5);
vScratch = clamp((vScratch * 10000.0) + 0.35, 0.0, 1.0);
col.rgb *= vScratch;
}
}
}
gl_FragColor = col;
})";
FadeShader::FadeShader() {
const char *fadeShader = R"(
#ifdef GL_ES
precision highp float;
#endif
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform sampler2D u_texture2;
uniform float u_timer;
uniform float u_fade;
uniform int u_fadeToSep;
uniform float u_movement;
void main() {
const float RADIUS = 0.75;
const float SOFTNESS = 0.45;
const float ScratchValue = 0.3;
vec2 uv = v_texCoords;
float pi2 = (3.142 * 2.0);
float intervals = 4.0;
uv.x += sin((u_timer + uv.y) * (pi2 * intervals)) * u_movement;
vec4 texColor1 = v_color * texture2D(u_texture, uv);
vec4 texColor2 = v_color * texture2D(u_texture2, uv);
if (u_fadeToSep != 0) {
float gray = dot(texColor2.rgb, vec3(0.299, 0.587, 0.114));
vec2 dist = vec2(uv.x - 0.5, uv.y - 0.5);
vec3 sepiaColor = vec3(gray, gray, gray) * vec3(0.9, 0.8, 0.6);
float len = dot(dist, dist);
float vignette = smoothstep(RADIUS, RADIUS - SOFTNESS, len);
vec3 sep = mix(texColor2.rgb, sepiaColor, 0.80) * vignette;
gl_FragColor.rgb = (texColor1.rgb * (1.0 - u_fade)) + (sep * u_fade);
} else {
gl_FragColor.rgb = (texColor1.rgb * (1.0 - u_fade)) + (texColor2.rgb * u_fade);
}
gl_FragColor.a = 1.0;
})";
init("fadeShader", vsrc, fadeShader);
GL_CALL(_textureLoc[0] = getUniformLocation("u_texture"));
GL_CALL(_textureLoc[1] = getUniformLocation("u_texture2"));
}
FadeShader::~FadeShader() {}
void FadeShader::applyUniforms() {
float movement = (sin(M_PI * _fade) * _movement);
setUniform("u_timer", _elapsed);
setUniform("u_fade", _fade);
setUniform("u_fadeToSep", _fadeToSepia ? 1 : 0);
setUniform("u_movement", movement);
}
int FadeShader::getNumTextures() { return 2; }
int FadeShader::getTexture(int index) {
switch (index) {
case 0:
return _texture1->id;
case 1:
return _texture2->id;
}
return 0;
}
int FadeShader::getTextureLoc(int index) { return _textureLoc[index]; }
void ShaderParams::updateShader() {
if (effect == RoomEffect::Sepia) {
Shader *shader = g_twp->getGfx().getShader();
shader->setUniform("RandomValue", randomValue, 5);
shader->setUniform("TimeLapse", timeLapse);
}
// } else if (effect == RoomEffect::Vhs) {
// Shader* shader = g_twp->getGfx().getShader();
// shader->setUniform("iGlobalTime", iGlobalTime);
// shader->setUniform("iNoiseThreshold", iNoiseThreshold);
// } else
else if (effect == RoomEffect::Ghost) {
Shader *shader = g_twp->getGfx().getShader();
shader->setUniform("iGlobalTime", iGlobalTime);
shader->setUniform("iFade", iFade);
shader->setUniform("wobbleIntensity", wobbleIntensity);
shader->setUniform3("shadows", shadows);
shader->setUniform3("midtones", midtones);
shader->setUniform3("highlights", highlights);
}
}
} // namespace Twp