| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- // zlib open source license
- //
- // Copyright (c) 2017 to 2023 David Forsgren Piuva
- //
- // 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.
- #ifndef DFPSR_RENDER_SHADER_RGBA_MULTIPLY
- #define DFPSR_RENDER_SHADER_RGBA_MULTIPLY
- #include <cstdio>
- #include <cstdint>
- #include <cassert>
- #include <algorithm>
- #include "Shader.h"
- #include "fillerTemplates.h"
- #include "../../image/Image.h"
- namespace dsr {
- struct RgbaMultiply_data {
- const TextureRgbaU8 *diffuseMap; // Mip-mapping is allowed for diffuse textures.
- const TextureRgbaU8 *lightMap; // Mip-mapping is not allowed for lightmaps, because it would increase the number of shaders to compile and still look worse.
- // Planar format with each vector representing the three triangle corners
- const TriangleTexCoords texCoords;
- const TriangleColors colors;
- // Normalize the color product by pre-multiplying the vertex colors
- float getVertexScale() {
- float result = 255.0f; // Scale from normalized to byte for the output
- if (texture_exists(*(this->diffuseMap))) {
- result *= 1.0f / 255.0f; // Normalize the diffuse map from 0..255 to 0..1 by dividing the vertex color
- }
- if (texture_exists(*(this->lightMap))) {
- result *= 1.0f / 255.0f; // Normalize the light map from 0..255 to 0..1 by dividing the vertex color
- }
- return result;
- }
- explicit RgbaMultiply_data(const TriangleInput &triangleInput) :
- diffuseMap(triangleInput.diffuseMap),
- lightMap(triangleInput.lightMap),
- texCoords(triangleInput.texCoords), colors(triangleInput.colors.getScaled(getVertexScale())) {}
- };
- // TODO: Simplify by merging boolean flags into named states.
- // A texture can be:
- // Non-existing
- // Of a single layer
- // Of multiple layers
- // Of enough layers to avoid clamping the mip index
- // A color can be:
- // White
- // Constant
- // Faded
- // TODO: Skip converting to and from float colors when only sampling one texture or color.
- // TODO: Because colors are converted to 16-bit channels during multiplication, the shader's return value might as well use a 16-bit color format that is faster to multiply and shift.
- // 8 low bits for visible light, and 8 high bits for spill from multiplications before shifting results 8 bits to the right.
- // High intensity vertex colors multiplied at the end can use the high range for 10-bit image formats.
- template <bool HAS_DIFFUSE_MAP, bool DIFFUSE_SINGLE_LAYER, bool HAS_LIGHT_MAP, bool HAS_VERTEX_FADING, bool COLORLESS>
- inline Rgba_F32<U32x4, F32x4> getPixels_2x2(void *data, const F32x4x3 &vertexWeights) {
- RgbaMultiply_data *assets = (RgbaMultiply_data*)data;
- if (HAS_DIFFUSE_MAP && !HAS_LIGHT_MAP && COLORLESS) {
- // Optimized for diffuse only
- F32x4 u1 = shaderMethods::interpolate(assets->texCoords.u1, vertexWeights);
- F32x4 v1 = shaderMethods::interpolate(assets->texCoords.v1, vertexWeights);
- return shaderMethods::sample_F32<Interpolation::BL, false, DIFFUSE_SINGLE_LAYER, false, false, false>(assets->diffuseMap, u1, v1);
- } else if (HAS_LIGHT_MAP && !HAS_DIFFUSE_MAP && COLORLESS) {
- // Optimized for light only
- F32x4 u2 = shaderMethods::interpolate(assets->texCoords.u2, vertexWeights);
- F32x4 v2 = shaderMethods::interpolate(assets->texCoords.v2, vertexWeights);
- return shaderMethods::sample_F32<Interpolation::BL, false, false, false, false, true>(assets->lightMap, u2, v2);
- } else {
- // Interpolate the vertex color
- Rgba_F32<U32x4, F32x4> color = HAS_VERTEX_FADING ?
- shaderMethods::interpolateVertexColor(assets->colors.red, assets->colors.green, assets->colors.blue, assets->colors.alpha, vertexWeights) :
- Rgba_F32<U32x4, F32x4>(F32x4(assets->colors.red.x), F32x4(assets->colors.green.x), F32x4(assets->colors.blue.x), F32x4(assets->colors.alpha.x));
- // Sample diffuse
- if (HAS_DIFFUSE_MAP) {
- F32x4 u1 = shaderMethods::interpolate(assets->texCoords.u1, vertexWeights);
- F32x4 v1 = shaderMethods::interpolate(assets->texCoords.v1, vertexWeights);
- color = color * shaderMethods::sample_F32<Interpolation::BL, false, DIFFUSE_SINGLE_LAYER, false, false, false>(assets->diffuseMap, u1, v1);
- }
- // Sample lightmap
- if (HAS_LIGHT_MAP) {
- F32x4 u2 = shaderMethods::interpolate(assets->texCoords.u2, vertexWeights);
- F32x4 v2 = shaderMethods::interpolate(assets->texCoords.v2, vertexWeights);
- color = color * shaderMethods::sample_F32<Interpolation::BL, false, false, false, false, true>(assets->lightMap, u2, v2);
- }
- return color;
- }
- }
- // The process method to take a function pointer to.
- // Must have the same signature as drawCallbackTemplate in Shader.h.
- static void processTriangle_RgbaMultiply(const TriangleInput &triangleInput, ImageRgbaU8 *colorBuffer, ImageF32 *depthBuffer, const ITriangle2D &triangle, const Projection &projection, const RowShape &shape, Filter filter) {
- // The pointers to textures may not be null, but can point to empty textures.
- RgbaMultiply_data data = RgbaMultiply_data(triangleInput);
- bool hasVertexFade = !(almostSame(data.colors.red) && almostSame(data.colors.green) && almostSame(data.colors.blue) && almostSame(data.colors.alpha));
- bool colorless = almostOne(data.colors.red) && almostOne(data.colors.green) && almostOne(data.colors.blue) && almostOne(data.colors.alpha);
- // TODO: Should non-existing textures use null pointers in the data, or pointers to empty textures?
- if (texture_exists(*(data.diffuseMap))) {
- bool hasDiffusePyramid = texture_hasPyramid(*(data.diffuseMap));
- // TODO: Avoid generating mip levels for the lightmap texture instead of hard-coding it to no mip levels.
- if (texture_exists(*(data.lightMap))) {
- if (hasVertexFade) { // DiffuseLightVertex
- if (hasDiffusePyramid) { // With mipmap
- fillShape(&data, getPixels_2x2<true, false, true, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- } else { // Without mipmap
- fillShape(&data, getPixels_2x2<true, true, true, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- }
- } else { // DiffuseLight
- if (hasDiffusePyramid) { // With mipmap
- fillShape(&data, getPixels_2x2<true, false, true, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- } else { // Without mipmap
- fillShape(&data, getPixels_2x2<true, true, true, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- }
- }
- } else {
- if (hasVertexFade) { // DiffuseVertex
- if (hasDiffusePyramid) { // With mipmap
- fillShape(&data, getPixels_2x2<false, false, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- } else { // Without mipmap
- fillShape(&data, getPixels_2x2<true, true, false, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- }
- } else {
- if (colorless) { // Diffuse without normalization
- if (hasDiffusePyramid) { // With mipmap
- fillShape(&data, getPixels_2x2<true, false, false, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- } else { // Without mipmap
- fillShape(&data, getPixels_2x2<true, true, false, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- }
- } else { // Diffuse
- if (hasDiffusePyramid) { // With mipmap
- fillShape(&data, getPixels_2x2<true, false, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- } else { // Without mipmap
- fillShape(&data, getPixels_2x2<true, true, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- }
- }
- }
- }
- } else {
- if (texture_exists(*(data.lightMap))) {
- if (hasVertexFade) { // LightVertex
- fillShape(&data, getPixels_2x2<false, false, true, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- } else {
- if (colorless) { // Light without normalization
- fillShape(&data, getPixels_2x2<false, false, true, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- } else { // Light
- fillShape(&data, getPixels_2x2<false, false, true, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- }
- }
- } else {
- if (hasVertexFade) { // Vertex
- fillShape(&data, getPixels_2x2<false, false, false, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- } else { // Single color
- fillShape(&data, getPixels_2x2<false, false, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
- }
- }
- }
- }
- }
- #endif
|