Skip to content
Permalink
Browse files

render: refactored blending, now works like on real HW

removed float operations from rasterizer
  • Loading branch information
JaCzekanski committed Sep 19, 2019
1 parent 6b89756 commit 93122bd075ecfec868404d501a4e1741213f352e
@@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <glm/glm.hpp>
#include "semi_transparency.h"
#include "utils/macros.h"
#include "utils/math.h"

@@ -34,54 +35,61 @@ union PSXColor {
INLINE uint16_t rev() const { return (r << 11) | (g << 6) | (b << 1) | k; }

PSXColor() : raw(0) {}

// 16bit word from VRAM
PSXColor(uint16_t color) : raw(color) {}

// 8bit input values
PSXColor(uint8_t r, uint8_t g, uint8_t b) {
this->r = r >> 3;
this->g = g >> 3;
this->b = b >> 3;
this->k = 0;
}

PSXColor operator+(const PSXColor& rhs) {
r = std::min(r + rhs.r, 31);
g = std::min(g + rhs.g, 31);
b = std::min(b + rhs.b, 31);
return *this;
}

PSXColor operator-(const PSXColor& rhs) {
r = std::max<int>(r - rhs.r, 0);
g = std::max<int>(g - rhs.g, 0);
b = std::max<int>(b - rhs.b, 0);
return *this;
}

PSXColor operator*(const float& rhs) {
r = (uint16_t)(r * rhs);
g = (uint16_t)(g * rhs);
b = (uint16_t)(b * rhs);
return *this;
}

PSXColor operator/(const int& rhs) {
r = (uint16_t)(r / rhs);
g = (uint16_t)(g / rhs);
b = (uint16_t)(b / rhs);
return *this;
}
// 5bit input values
PSXColor(uint8_t r, uint8_t g, uint8_t b, uint8_t k) : r(r), g(g), b(b), k(k) {}

PSXColor operator>>(const int& sh) {
r >>= sh;
g >>= sh;
b >>= sh;
return *this;
PSXColor operator*(const glm::ivec3& rhs) const {
return PSXColor( //
clamp_top<uint8_t>((rhs.r * r) >> 7, 31), //
clamp_top<uint8_t>((rhs.g * g) >> 7, 31), //
clamp_top<uint8_t>((rhs.b * b) >> 7, 31), //
k //
);
}

PSXColor operator*(const glm::vec3& rhs) {
r = std::min<uint16_t>((uint16_t)(rhs.r * r), 31);
g = std::min<uint16_t>((uint16_t)(rhs.g * g), 31);
b = std::min<uint16_t>((uint16_t)(rhs.b * b), 31);
return *this;
INLINE static PSXColor blend(const PSXColor& bg, const PSXColor& c, const gpu::SemiTransparency& transparency) {
switch (transparency) {
case gpu::SemiTransparency::Bby2plusFby2:
return PSXColor( //
clamp_top((bg.r + c.r) >> 1, 31), //
clamp_top((bg.g + c.g) >> 1, 31), //
clamp_top((bg.b + c.b) >> 1, 31), //
0 //
);
case gpu::SemiTransparency::BplusF:
return PSXColor( //
clamp_top(bg.r + c.r, 31), //
clamp_top(bg.g + c.g, 31), //
clamp_top(bg.b + c.b, 31), //
0 //
);
case gpu::SemiTransparency::BminusF:
return PSXColor( //
clamp_bottom(bg.r - c.r, 0), //
clamp_bottom(bg.g - c.g, 0), //
clamp_bottom(bg.b - c.b, 0), //
0 //
);
case gpu::SemiTransparency::BplusFby4:
return PSXColor( //
clamp_top(bg.r + (c.r >> 2), 31), //
clamp_top(bg.g + (c.g >> 2), 31), //
clamp_top(bg.b + (c.b >> 2), 31), //
0 //
);
}
}
};

@@ -7,7 +7,6 @@
#define VRAM ((uint16_t(*)[gpu::VRAM_WIDTH])gpu->vram.data())

void Render::drawLine(gpu::GPU* gpu, const primitive::Line& line) {
using Transparency = gpu::SemiTransparency;
const auto transparency = gpu->gp0_e1.semiTransparency;
const bool checkMaskBeforeDraw = gpu->gp0_e6.checkMaskBeforeDraw;
const bool setMaskWhileDrawing = gpu->gp0_e6.setMaskWhileDrawing;
@@ -73,14 +72,8 @@ void Render::drawLine(gpu::GPU* gpu, const primitive::Line& line) {
c = PSXColor(col.r, col.g, col.b);
}

// TODO: This code repeats 3 times, make it more generic
if (line.isSemiTransparent) {
switch (transparency) {
case Transparency::Bby2plusFby2: c = (bg >> 1) + (c >> 1); break;
case Transparency::BplusF: c = bg + c; break;
case Transparency::BminusF: c = bg - c; break;
case Transparency::BplusFby4: c = bg + (c >> 2); break;
}
c = PSXColor::blend(bg, c, transparency);
}

c.k |= bg.k | setMaskWhileDrawing;
@@ -4,8 +4,8 @@
#include "utils/macros.h"

using glm::ivec2;
using glm::ivec3;
using glm::uvec2;
using glm::vec3;
using gpu::GPU;

#undef VRAM
@@ -28,7 +28,6 @@ INLINE glm::uvec2 calculateTexel(glm::ivec2 tex, const gpu::GP0_E2 textureWindow
template <ColorDepth bits>
INLINE void rectangle(GPU* gpu, const primitive::Rect& rect) {
// Extract common GPU state
using Transparency = gpu::SemiTransparency;
const auto transparency = gpu->gp0_e1.semiTransparency;
const bool checkMaskBeforeDraw = gpu->gp0_e6.checkMaskBeforeDraw;
const bool setMaskWhileDrawing = gpu->gp0_e6.setMaskWhileDrawing;
@@ -83,18 +82,12 @@ INLINE void rectangle(GPU* gpu, const primitive::Rect& rect) {
if (c.raw == 0x0000) continue;

if (isBlended) {
vec3 brightness = vec3(rect.color.r, rect.color.g, rect.color.b) / 255.f;
c = c * (brightness * 2.f);
c = c * ivec3(rect.color.r, rect.color.g, rect.color.b);
}
}

if (rect.isSemiTransparent && (!isTextured || c.k)) {
switch (transparency) {
case Transparency::Bby2plusFby2: c = (bg >> 1) + (c >> 1); break;
case Transparency::BplusF: c = bg + c; break;
case Transparency::BminusF: c = bg - c; break;
case Transparency::BplusFby4: c = bg + (c >> 2); break;
}
c = PSXColor::blend(bg, c, transparency);
}

c.k |= bg.k | setMaskWhileDrawing;
@@ -9,7 +9,6 @@
using glm::ivec2;
using glm::ivec3;
using glm::uvec2;
using glm::vec3;
using gpu::GPU;
using gpu::Vertex;

@@ -67,7 +66,6 @@ static bool isTopLeft(const ivec2 e) { return e.y < 0 || (e.y == 0 && e.x < 0);
template <ColorDepth bits>
void rasterizeTriangle(GPU* gpu, const primitive::Triangle& triangle) {
// Extract common GPU state
using Transparency = gpu::SemiTransparency;
const auto transparency = triangle.transparency;
const bool checkMaskBeforeDraw = gpu->gp0_e6.checkMaskBeforeDraw;
const bool setMaskWhileDrawing = gpu->gp0_e6.setMaskWhileDrawing;
@@ -133,10 +131,8 @@ void rasterizeTriangle(GPU* gpu, const primitive::Triangle& triangle) {
};

ivec3 COLOR[3];
vec3 fcolor[3];
for (int i = 0; i < 3; i++) {
COLOR[i] = ivec3(triangle.v[i].color.r, triangle.v[i].color.g, triangle.v[i].color.b);
fcolor[i] = vec3(COLOR[i]) / 255.f;
}

ivec2 p;
@@ -163,32 +159,25 @@ void rasterizeTriangle(GPU* gpu, const primitive::Triangle& triangle) {
if (c.raw == 0x0000) goto DONE;

if (isBlended) {
vec3 brightness;

ivec3 brightness;
if (isGouroudShaded) {
// TODO: Get rid of float colors
brightness = vec3( //
(s.x * fcolor[0].r + s.y * fcolor[1].r + s.z * fcolor[2].r), //
(s.x * fcolor[0].g + s.y * fcolor[1].g + s.z * fcolor[2].g), //
(s.x * fcolor[0].b + s.y * fcolor[1].b + s.z * fcolor[2].b) //
brightness = ivec3( //
(s.x * COLOR[0].r + s.y * COLOR[1].r + s.z * COLOR[2].r), //
(s.x * COLOR[0].g + s.y * COLOR[1].g + s.z * COLOR[2].g), //
(s.x * COLOR[0].b + s.y * COLOR[1].b + s.z * COLOR[2].b) //
);
brightness /= area;
} else { // Flat shading
brightness = fcolor[0];
brightness = COLOR[0];
}

c = c * (brightness * 2.f);
c = c * brightness;
}
// TODO: Textured polygons are not dithered
}

if (isSemiTransparent && (!isTextured || c.k)) {
switch (transparency) {
case Transparency::Bby2plusFby2: c = (bg >> 1) + (c >> 1); break;
case Transparency::BplusF: c = bg + c; break;
case Transparency::BminusF: c = bg - c; break;
case Transparency::BplusFby4: c = bg + (c >> 2); break;
}
c = PSXColor::blend(bg, c, transparency);
}

c.k |= bg.k | setMaskWhileDrawing;
@@ -1,5 +1,4 @@
#pragma once
#include <algorithm>

template <typename T>
T clamp(T number, size_t range) {
@@ -9,7 +8,23 @@ T clamp(T number, size_t range) {

template <typename T>
T clamp(T v, T min, T max) {
return std::min(std::max(v, min), max);
if (v > max) {
return max;
} else if (v < min) {
return min;
} else {
return v;
}
}

template <typename T>
T clamp_top(T v, T top) {
return v > top ? top : v;
}

template <typename T>
T clamp_bottom(T v, T bottom) {
return v < bottom ? bottom : v;
}

inline float lerp(float a, float b, float t) { return a + t * (b - a); }

0 comments on commit 93122bd

Please sign in to comment.
You can’t perform that action at this time.