Ver Fonte

feat: 添加贴图的绘制方式

nicetry12138 há 1 ano atrás
pai
commit
df34f7a9d6

BIN
OpenGL/Image/032.png


BIN
OpenGL/Image/033.png


+ 63 - 0
OpenGL/README.md

@@ -1271,5 +1271,68 @@ _canvas->drawTriangle(
 | --- | --- | 
 | ![](Image/031.png) | ![](Image/030.png) |
 
+如果纹理坐标 UV 值大于 1,有三种解决方案
+
+1. 纹理重复(Tiling or Wrapping)
+   - 纹理重复是一种常见的处理方式,当UV坐标超过1时,纹理在模型上重复出现。这就像纹理图像在每个方向上都被平铺了:
+   - 水平重复:当U坐标大于1时,纹理在水平方向上重复。例如,U坐标为1.2时,显示的是原始纹理的20%和80%的重复部分
+   - 垂直重复:当V坐标大于1时,纹理在垂直方向上重复。例如,V坐标为2.3时,纹理将完整地重复两次,第三次显示30%的初始部分
+
+2. 纹理钳制(Clamping)
+   - 纹理钳制是另一种处理UV坐标超界的方法。在这种模式下,纹理的边缘像素会被拉伸以填充超出[0, 1]范围的区域:
+   - 如果U或V坐标小于0,它将被设置为0
+   - 如果U或V坐标大于1,它将被设置为1
+   - 结果是,纹理边缘的最后一个像素被延伸到模型的剩余部分
+   - 这种方法适用于你不希望纹理在模型上重复的情况,常见于需要清晰边界的纹理映射,如墙面、地板或其他需要明确边缘的场景。
+
+3. 纹理镜像(Mirrored Repeat)
+   - 纹理镜像是一种特殊的重复处理方式,当UV坐标超过1时,纹理会镜像翻转而不是简单地重复:
+   - 这意味着每次纹理坐标超过一个整数值时(例如,从1到2,从2到3),纹理图像都会被翻转(镜像)
+   - 这种方式可以避免普通重复可能带来的可见接缝,提供更自然的视觉过渡
+
+- 纹理重复:适用于需要连续无缝纹理的场景,如地板砖、墙纸或任何需要连续图案的表面
+- 纹理钳制:适合那些纹理边缘需要明确显示的模型,如画框、屏幕边界等
+- 纹理镜像:适用于需要避免可见接缝而又不希望简单重复纹理的场景,如服装、家具布料等
+
+```cpp
+RGBA Image::GetColorByUV(floatV2 inUV, TEXTURE_TYPE inType) const
+{
+    int x = inUV.x * m_Width;
+    int y = inUV.y * m_Height;
+
+    switch (inType)
+    {
+    case GT::Image::TX_CLAMP_TO_EDGE: 
+        x = GT::UTool::clamp(x, 0, m_Width - 1);
+        y = GT::UTool::clamp(y, 0, m_Height - 1);
+        break;
+    case GT::Image::TX_REPEAT:
+        x %= m_Width;
+        y %= m_Height;
+        break;
+    default:
+        break;
+    }
+
+    return GetColor(x, y);
+}
+```
+
+只需要在计算坐标的时候根据不同的情况对坐标做一些修改即可
+
+```cpp
+_canvas->drawTriangle(
+    GT::Point(0, wHeight / 2, GT::RGBA(), GT::floatV2(0, 0)),
+    GT::Point(wWidth, 0, GT::RGBA(), GT::floatV2(2, 0)),
+    GT::Point(wWidth / 2, wHeight, GT::RGBA(), GT::floatV2(1, 2))
+);
+```
+
+| 纹理重复 | 纹理钳制 |
+| --- | --- |
+| ![](Image/033.png) | ![](Image/032.png) |
+
+对于贴图来说,我们会发现如果三角形过大,出现与图片放大时使用最近邻插值算法一样的锯齿情况,这是因为我们在通过 UV 坐标计算像素颜色时的方式与图片放大最近邻算法相似,同理我们可以在这里使用双线性插值的算法优化贴图计算
+
 ## 图形学状态机接口封装
 

+ 6 - 1
OpenGL/src/WindowsProjectTest/WindowsProjectTest/Canvas.cpp

@@ -57,7 +57,7 @@ namespace GT {
 			if (m_enableTexture && m_texture != nullptr) {
 				// 开启贴图 并且贴图有效 使用贴图颜色
 				floatV2 uv = uvLerp(pt1.m_uv, pt2.m_uv, (float)index / sumStep);
-				pointColor = m_texture->GetColorByUV(uv);
+				pointColor = m_texture->GetColorByUV(uv, m_textureType);
 			}
 			else {
 				pointColor = colorLerp(pt1.m_color, pt2.m_color, (float)index / sumStep);
@@ -203,6 +203,11 @@ namespace GT {
 		m_texture = inImage;
 	}
 
+	void Canvas::setTextureType(Image::TEXTURE_TYPE inType)
+	{
+		m_textureType = inType;
+	}
+
 	void Canvas::drawTriangleFlat(const Point& pt1, const Point& pt2, const Point& pt)
 	{
 		float k1 = 0.0f;

+ 5 - 1
OpenGL/src/WindowsProjectTest/WindowsProjectTest/Canvas.h

@@ -3,8 +3,9 @@
 #include <string.h>
 #include <vector>
 
+#include "Image.h"
+
 namespace GT {
-	class Image;
 
 	// 操作画布的类
 	class Canvas
@@ -19,6 +20,7 @@ namespace GT {
 
 		bool m_enableTexture = true;	// 是否启用纹理贴图
 		Image* m_texture{ nullptr };	// 绘制剩下顶点的时候 使用那种图片
+		Image::TEXTURE_TYPE m_textureType = Image::TEXTURE_TYPE::TX_REPEAT;	// 纹理绘制
 	public: 
 		Canvas(int _width, int _height, void* _buffer) {
 			if (_width <= 0 || _height <= 0) {
@@ -71,6 +73,8 @@ namespace GT {
 
 		void bindTexture(Image* inImage);
 
+		void setTextureType(Image::TEXTURE_TYPE inType);
+
 	protected:
 		// 平底平顶三角形绘制 pt1 和 pt2 是平底或平顶的两点,pt 是单独的一个顶点
 		void drawTriangleFlat(const Point& pt1, const Point& pt2, const Point& pt);

+ 18 - 3
OpenGL/src/WindowsProjectTest/WindowsProjectTest/Image.cpp

@@ -17,10 +17,25 @@ namespace GT {
 		return m_Data[y * m_Width + x];
 	}
 
-	RGBA Image::GetColorByUV(floatV2 inUV) const
+	RGBA Image::GetColorByUV(floatV2 inUV, TEXTURE_TYPE inType) const
 	{
-		int x = inUV.x * GetWidth();
-		int y = inUV.y * GetHeight();
+		int x = inUV.x * m_Width;
+		int y = inUV.y * m_Height;
+
+		switch (inType)
+		{
+		case GT::Image::TX_CLAMP_TO_EDGE: 
+			x = GT::UTool::clamp(x, 0, m_Width - 1);
+			y = GT::UTool::clamp(y, 0, m_Height - 1);
+			break;
+		case GT::Image::TX_REPEAT:
+			x %= m_Width;
+			y %= m_Height;
+			break;
+		default:
+			break;
+		}
+
 		return GetColor(x, y);
 	}
 

BIN
OpenGL/src/WindowsProjectTest/WindowsProjectTest/Image.h


+ 3 - 2
OpenGL/src/WindowsProjectTest/WindowsProjectTest/WindowsProjectTest.cpp

@@ -64,11 +64,12 @@ void Render() {
     // 测试贴图
     _canvas->bindTexture(_bgImage);
     _canvas->enableTexture(true);
+    _canvas->setTextureType(GT::Image::TX_CLAMP_TO_EDGE);
 
 	_canvas->drawTriangle(
 		GT::Point(0, wHeight / 2, GT::RGBA(), GT::floatV2(0, 0)),
-		GT::Point(wWidth, 0, GT::RGBA(), GT::floatV2(1, 0)),
-		GT::Point(wWidth / 2, wHeight, GT::RGBA(), GT::floatV2(0.5, 1))
+		GT::Point(wWidth, 0, GT::RGBA(), GT::floatV2(2, 0)),
+		GT::Point(wWidth / 2, wHeight, GT::RGBA(), GT::floatV2(1, 2))
     );
 
 	// 将 hMem 的数据一次写入到 hDC 中