RgbaMultiply.h 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2017 to 2023 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #ifndef DFPSR_RENDER_SHADER_RGBA_MULTIPLY
  24. #define DFPSR_RENDER_SHADER_RGBA_MULTIPLY
  25. #include <cstdio>
  26. #include <cstdint>
  27. #include <cassert>
  28. #include <algorithm>
  29. #include "Shader.h"
  30. #include "fillerTemplates.h"
  31. #include "../../image/ImageRgbaU8.h"
  32. namespace dsr {
  33. struct RgbaMultiply_data {
  34. const TextureRgba *diffuseMap; // Mip-mapping is allowed for diffuse textures.
  35. const TextureRgba *lightMap; // Mip-mapping is not allowed for lightmaps, because it would increase the number of shaders to compile and still look worse.
  36. // Planar format with each vector representing the three triangle corners
  37. const TriangleTexCoords texCoords;
  38. const TriangleColors colors;
  39. // Normalize the color product by pre-multiplying the vertex colors
  40. float getVertexScale() {
  41. float result = 255.0f; // Scale from normalized to byte for the output
  42. if (this->diffuseMap) {
  43. result *= 1.0f / 255.0f; // Normalize the diffuse map from 0..255 to 0..1 by dividing the vertex color
  44. }
  45. if (this->lightMap) {
  46. result *= 1.0f / 255.0f; // Normalize the light map from 0..255 to 0..1 by dividing the vertex color
  47. }
  48. return result;
  49. }
  50. explicit RgbaMultiply_data(const TriangleInput &triangleInput) :
  51. diffuseMap(triangleInput.diffuseImage ? &(triangleInput.diffuseImage->texture) : nullptr),
  52. lightMap(triangleInput.lightImage ? &(triangleInput.lightImage->texture) : nullptr),
  53. texCoords(triangleInput.texCoords), colors(triangleInput.colors.getScaled(getVertexScale())) {
  54. // Texture coordinates must be on the positive side to allow using truncation as a floor function
  55. if (this->diffuseMap) {
  56. assert(this->diffuseMap != nullptr); // Cannot sample null
  57. assert(this->diffuseMap->exists()); // Cannot sample regular images
  58. }
  59. if (this->lightMap) {
  60. assert(this->lightMap != nullptr); // Cannot sample null
  61. assert(this->lightMap->exists()); // Cannot sample regular images
  62. }
  63. }
  64. };
  65. template <bool HAS_DIFFUSE_MAP, bool HAS_LIGHT_MAP, bool HAS_VERTEX_FADING, bool COLORLESS, bool DISABLE_MIPMAP>
  66. static Rgba_F32 getPixels_2x2(void *data, const F32x4x3 &vertexWeights) {
  67. if (HAS_DIFFUSE_MAP && !HAS_LIGHT_MAP && COLORLESS) {
  68. // Optimized for diffuse only
  69. F32x4 u1 = shaderMethods::interpolate(((RgbaMultiply_data*)data)->texCoords.u1, vertexWeights);
  70. F32x4 v1 = shaderMethods::interpolate(((RgbaMultiply_data*)data)->texCoords.v1, vertexWeights);
  71. return shaderMethods::sample_F32<Interpolation::BL, DISABLE_MIPMAP, false>(((RgbaMultiply_data*)data)->diffuseMap, u1, v1);
  72. } else if (HAS_LIGHT_MAP && !HAS_DIFFUSE_MAP && COLORLESS) {
  73. // Optimized for light only
  74. F32x4 u2 = shaderMethods::interpolate(((RgbaMultiply_data*)data)->texCoords.u2, vertexWeights);
  75. F32x4 v2 = shaderMethods::interpolate(((RgbaMultiply_data*)data)->texCoords.v2, vertexWeights);
  76. return shaderMethods::sample_F32<Interpolation::BL, true, false>(((RgbaMultiply_data*)data)->lightMap, u2, v2);
  77. } else {
  78. // Interpolate the vertex color
  79. Rgba_F32 color = HAS_VERTEX_FADING ?
  80. shaderMethods::interpolateVertexColor(((RgbaMultiply_data*)data)->colors.red, ((RgbaMultiply_data*)data)->colors.green, ((RgbaMultiply_data*)data)->colors.blue, ((RgbaMultiply_data*)data)->colors.alpha, vertexWeights) :
  81. Rgba_F32(F32x4(((RgbaMultiply_data*)data)->colors.red.x), F32x4(((RgbaMultiply_data*)data)->colors.green.x), F32x4(((RgbaMultiply_data*)data)->colors.blue.x), F32x4(((RgbaMultiply_data*)data)->colors.alpha.x));
  82. // Sample diffuse
  83. if (HAS_DIFFUSE_MAP) {
  84. F32x4 u1 = shaderMethods::interpolate(((RgbaMultiply_data*)data)->texCoords.u1, vertexWeights);
  85. F32x4 v1 = shaderMethods::interpolate(((RgbaMultiply_data*)data)->texCoords.v1, vertexWeights);
  86. color = color * shaderMethods::sample_F32<Interpolation::BL, DISABLE_MIPMAP, false>(((RgbaMultiply_data*)data)->diffuseMap, u1, v1);
  87. }
  88. // Sample lightmap
  89. if (HAS_LIGHT_MAP) {
  90. F32x4 u2 = shaderMethods::interpolate(((RgbaMultiply_data*)data)->texCoords.u2, vertexWeights);
  91. F32x4 v2 = shaderMethods::interpolate(((RgbaMultiply_data*)data)->texCoords.v2, vertexWeights);
  92. color = color * shaderMethods::sample_F32<Interpolation::BL, true, false>(((RgbaMultiply_data*)data)->lightMap, u2, v2);
  93. }
  94. return color;
  95. }
  96. }
  97. // The process method to take a function pointer to.
  98. // Must have the same signature as drawCallbackTemplate in Shader.h.
  99. static void processTriangle_RgbaMultiply(const TriangleInput &triangleInput, ImageRgbaU8Impl *colorBuffer, ImageF32Impl *depthBuffer, const ITriangle2D &triangle, const Projection &projection, const RowShape &shape, Filter filter) {
  100. RgbaMultiply_data data = RgbaMultiply_data(triangleInput);
  101. bool hasVertexFade = !(almostSame(data.colors.red) && almostSame(data.colors.green) && almostSame(data.colors.blue) && almostSame(data.colors.alpha));
  102. bool colorless = almostOne(data.colors.red) && almostOne(data.colors.green) && almostOne(data.colors.blue) && almostOne(data.colors.alpha);
  103. if (data.diffuseMap) {
  104. bool hasDiffusePyramid = data.diffuseMap->hasMipBuffer();
  105. if (data.lightMap) {
  106. if (hasVertexFade) { // DiffuseLightVertex
  107. if (hasDiffusePyramid) { // With mipmap
  108. fillShape(&data, getPixels_2x2<true, true, true, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  109. } else { // Without mipmap
  110. fillShape(&data, getPixels_2x2<true, true, true, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  111. }
  112. } else { // DiffuseLight
  113. if (hasDiffusePyramid) { // With mipmap
  114. fillShape(&data, getPixels_2x2<true, true, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  115. } else { // Without mipmap
  116. fillShape(&data, getPixels_2x2<true, true, false, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  117. }
  118. }
  119. } else {
  120. if (hasVertexFade) { // DiffuseVertex
  121. if (hasDiffusePyramid) { // With mipmap
  122. fillShape(&data, getPixels_2x2<false, false, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  123. } else { // Without mipmap
  124. fillShape(&data, getPixels_2x2<true, false, true, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  125. }
  126. } else {
  127. if (colorless) { // Diffuse without normalization
  128. if (hasDiffusePyramid) { // With mipmap
  129. fillShape(&data, getPixels_2x2<true, false, false, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  130. } else { // Without mipmap
  131. fillShape(&data, getPixels_2x2<true, false, false, true, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  132. }
  133. } else { // Diffuse
  134. if (hasDiffusePyramid) { // With mipmap
  135. fillShape(&data, getPixels_2x2<true, false, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  136. } else { // Without mipmap
  137. fillShape(&data, getPixels_2x2<true, false, false, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  138. }
  139. }
  140. }
  141. }
  142. } else {
  143. if (data.lightMap) {
  144. if (hasVertexFade) { // LightVertex
  145. fillShape(&data, getPixels_2x2<false, true, true, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  146. } else {
  147. if (colorless) { // Light without normalization
  148. fillShape(&data, getPixels_2x2<false, true, false, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  149. } else { // Light
  150. fillShape(&data, getPixels_2x2<false, true, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  151. }
  152. }
  153. } else {
  154. if (hasVertexFade) { // Vertex
  155. fillShape(&data, getPixels_2x2<false, false, true, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  156. } else { // Single color
  157. fillShape(&data, getPixels_2x2<false, false, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  158. }
  159. }
  160. }
  161. }
  162. }
  163. #endif