RgbaMultiply.h 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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/Image.h"
  32. namespace dsr {
  33. struct RgbaMultiply_data {
  34. const TextureRgbaU8 *diffuseMap; // Mip-mapping is allowed for diffuse textures.
  35. const TextureRgbaU8 *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 (texture_exists(*(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 (texture_exists(*(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.diffuseMap),
  52. lightMap(triangleInput.lightMap),
  53. texCoords(triangleInput.texCoords), colors(triangleInput.colors.getScaled(getVertexScale())) {}
  54. };
  55. // TODO: Simplify by merging boolean flags into named states.
  56. // A texture can be:
  57. // Non-existing
  58. // Of a single layer
  59. // Of multiple layers
  60. // Of enough layers to avoid clamping the mip index
  61. // A color can be:
  62. // White
  63. // Constant
  64. // Faded
  65. // TODO: Skip converting to and from float colors when only sampling one texture or color.
  66. // 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.
  67. // 8 low bits for visible light, and 8 high bits for spill from multiplications before shifting results 8 bits to the right.
  68. // High intensity vertex colors multiplied at the end can use the high range for 10-bit image formats.
  69. template <bool HAS_DIFFUSE_MAP, bool DIFFUSE_SINGLE_LAYER, bool HAS_LIGHT_MAP, bool HAS_VERTEX_FADING, bool COLORLESS>
  70. inline Rgba_F32<U32x4, F32x4> getPixels_2x2(void *data, const F32x4x3 &vertexWeights) {
  71. RgbaMultiply_data *assets = (RgbaMultiply_data*)data;
  72. if (HAS_DIFFUSE_MAP && !HAS_LIGHT_MAP && COLORLESS) {
  73. // Optimized for diffuse only
  74. F32x4 u1 = shaderMethods::interpolate(assets->texCoords.u1, vertexWeights);
  75. F32x4 v1 = shaderMethods::interpolate(assets->texCoords.v1, vertexWeights);
  76. return shaderMethods::sample_F32<Interpolation::BL, false, DIFFUSE_SINGLE_LAYER, false, false, false>(assets->diffuseMap, u1, v1);
  77. } else if (HAS_LIGHT_MAP && !HAS_DIFFUSE_MAP && COLORLESS) {
  78. // Optimized for light only
  79. F32x4 u2 = shaderMethods::interpolate(assets->texCoords.u2, vertexWeights);
  80. F32x4 v2 = shaderMethods::interpolate(assets->texCoords.v2, vertexWeights);
  81. return shaderMethods::sample_F32<Interpolation::BL, false, false, false, false, true>(assets->lightMap, u2, v2);
  82. } else {
  83. // Interpolate the vertex color
  84. Rgba_F32<U32x4, F32x4> color = HAS_VERTEX_FADING ?
  85. shaderMethods::interpolateVertexColor(assets->colors.red, assets->colors.green, assets->colors.blue, assets->colors.alpha, vertexWeights) :
  86. Rgba_F32<U32x4, F32x4>(F32x4(assets->colors.red.x), F32x4(assets->colors.green.x), F32x4(assets->colors.blue.x), F32x4(assets->colors.alpha.x));
  87. // Sample diffuse
  88. if (HAS_DIFFUSE_MAP) {
  89. F32x4 u1 = shaderMethods::interpolate(assets->texCoords.u1, vertexWeights);
  90. F32x4 v1 = shaderMethods::interpolate(assets->texCoords.v1, vertexWeights);
  91. color = color * shaderMethods::sample_F32<Interpolation::BL, false, DIFFUSE_SINGLE_LAYER, false, false, false>(assets->diffuseMap, u1, v1);
  92. }
  93. // Sample lightmap
  94. if (HAS_LIGHT_MAP) {
  95. F32x4 u2 = shaderMethods::interpolate(assets->texCoords.u2, vertexWeights);
  96. F32x4 v2 = shaderMethods::interpolate(assets->texCoords.v2, vertexWeights);
  97. color = color * shaderMethods::sample_F32<Interpolation::BL, false, false, false, false, true>(assets->lightMap, u2, v2);
  98. }
  99. return color;
  100. }
  101. }
  102. // The process method to take a function pointer to.
  103. // Must have the same signature as drawCallbackTemplate in Shader.h.
  104. static void processTriangle_RgbaMultiply(const TriangleInput &triangleInput, ImageRgbaU8 *colorBuffer, ImageF32 *depthBuffer, const ITriangle2D &triangle, const Projection &projection, const RowShape &shape, Filter filter) {
  105. // The pointers to textures may not be null, but can point to empty textures.
  106. RgbaMultiply_data data = RgbaMultiply_data(triangleInput);
  107. bool hasVertexFade = !(almostSame(data.colors.red) && almostSame(data.colors.green) && almostSame(data.colors.blue) && almostSame(data.colors.alpha));
  108. bool colorless = almostOne(data.colors.red) && almostOne(data.colors.green) && almostOne(data.colors.blue) && almostOne(data.colors.alpha);
  109. // TODO: Should non-existing textures use null pointers in the data, or pointers to empty textures?
  110. if (texture_exists(*(data.diffuseMap))) {
  111. bool hasDiffusePyramid = texture_hasPyramid(*(data.diffuseMap));
  112. // TODO: Avoid generating mip levels for the lightmap texture instead of hard-coding it to no mip levels.
  113. if (texture_exists(*(data.lightMap))) {
  114. if (hasVertexFade) { // DiffuseLightVertex
  115. if (hasDiffusePyramid) { // With mipmap
  116. fillShape(&data, getPixels_2x2<true, false, true, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  117. } else { // Without mipmap
  118. fillShape(&data, getPixels_2x2<true, true, true, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  119. }
  120. } else { // DiffuseLight
  121. if (hasDiffusePyramid) { // With mipmap
  122. fillShape(&data, getPixels_2x2<true, false, true, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  123. } else { // Without mipmap
  124. fillShape(&data, getPixels_2x2<true, true, true, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  125. }
  126. }
  127. } else {
  128. if (hasVertexFade) { // DiffuseVertex
  129. if (hasDiffusePyramid) { // With mipmap
  130. fillShape(&data, getPixels_2x2<false, false, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  131. } else { // Without mipmap
  132. fillShape(&data, getPixels_2x2<true, true, false, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  133. }
  134. } else {
  135. if (colorless) { // Diffuse without normalization
  136. if (hasDiffusePyramid) { // With mipmap
  137. fillShape(&data, getPixels_2x2<true, false, false, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  138. } else { // Without mipmap
  139. fillShape(&data, getPixels_2x2<true, true, false, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  140. }
  141. } else { // Diffuse
  142. if (hasDiffusePyramid) { // With mipmap
  143. fillShape(&data, getPixels_2x2<true, false, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  144. } else { // Without mipmap
  145. fillShape(&data, getPixels_2x2<true, true, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  146. }
  147. }
  148. }
  149. }
  150. } else {
  151. if (texture_exists(*(data.lightMap))) {
  152. if (hasVertexFade) { // LightVertex
  153. fillShape(&data, getPixels_2x2<false, false, true, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  154. } else {
  155. if (colorless) { // Light without normalization
  156. fillShape(&data, getPixels_2x2<false, false, true, false, true>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  157. } else { // Light
  158. fillShape(&data, getPixels_2x2<false, false, true, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  159. }
  160. }
  161. } else {
  162. if (hasVertexFade) { // Vertex
  163. fillShape(&data, getPixels_2x2<false, false, false, true, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  164. } else { // Single color
  165. fillShape(&data, getPixels_2x2<false, false, false, false, false>, colorBuffer, depthBuffer, triangle, projection, shape, filter);
  166. }
  167. }
  168. }
  169. }
  170. }
  171. #endif