Parcourir la source

feat: 添加多边形绘制贴图

nicetry12138 il y a 1 an
Parent
commit
7ac32cc0b1

BIN
OpenGL/Image/030.png


BIN
OpenGL/Image/031.png


+ 75 - 0
OpenGL/README.md

@@ -1194,7 +1194,82 @@ UV坐标系是在计算机图形学中用于纹理映射的二维坐标系统。
 
 以上顶点为例,该点的颜色就是贴图中 $X = U*Width_{image}, Y = V*Height_{image}$ 像素点的颜色
 
+于是我们给点增加新的属性 `m_uv` 用与标记该点在贴图中的 UV 坐标
 
+```cpp
+struct Point
+{
+public:
+    int m_x;
+    int m_y;
+    RGBA m_color;
+    floatV2 m_uv;
+}
+```
+
+同时还要给图片提供一个新的接口 `GetColorByUV` 用与通过 `UV` 坐标获得对应的颜色
+
+```cpp
+RGBA Image::GetColorByUV(floatV2 inUV) const
+{
+    int x = inUV.x * GetWidth();
+    int y = inUV.y * GetHeight();
+    return GetColor(x, y);
+}
+```
+
+然后添加在 `Canvas` 添加一个标记位用与标记是否启用贴图
+
+```cpp
+bool m_enableTexture = true;	// 是否启用纹理贴图
+Image* m_texture{ nullptr };	// 绘制剩下顶点的时候 使用那种图片
+```
+
+在绘制多边形的时候可以通过设置顶点的 `UV` 坐标,进而知道绘制的颜色,三角形内像素的 `UV` 通过顶点的 `UV` 插值可以计算,与三角形内像素的颜色可以通过三顶点颜色插值计算出来一样
+
+```cpp
+// UV 插值
+inline floatV2 Canvas::uvLerp(const floatV2 inUV1, floatV2 inUV2, float inScale)
+{
+    floatV2 result;
+    result.x = inUV1.x + (inUV2.x - inUV1.x) * inScale;
+    result.y = inUV1.y + (inUV2.y - inUV1.y) * inScale;
+    return result;
+}
+```
+
+通过 `UV` 插值计算得到三角形内像素的 `UV`,就可以通过 `UV` 获得贴图的颜色,进而设置到像素上
+
+```cpp
+RGBA pointColor;
+
+if (m_enableTexture && m_texture != nullptr) {
+    // 开启贴图 并且贴图有效 使用贴图颜色
+    floatV2 uv = uvLerp(pt1.m_uv, pt2.m_uv, (float)index / sumStep);
+    pointColor = m_texture->GetColorByUV(uv);
+}
+else {
+    pointColor = colorLerp(pt1.m_color, pt2.m_color, (float)index / sumStep);
+}
+drawPoint(Point(xNow, yNow, pointColor));
+```
+
+设置顶点信息,设置贴图后,绘制三角形
+
+```cpp
+_canvas->bindTexture(_bgImage);
+_canvas->enableTexture(true);
+
+_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))
+);
+```
+
+| 原图 | 贴图 |
+| --- | --- | 
+| ![](Image/031.png) | ![](Image/030.png) |
 
 ## 图形学状态机接口封装
 

+ 45 - 7
OpenGL/src/WindowsProjectTest/WindowsProjectTest/Canvas.cpp

@@ -52,7 +52,16 @@ namespace GT {
 			//std::wstring str = L"index = " + std::to_wstring(index) + L" xNow = " + std::to_wstring(xNow) + L", yNow = " + std::to_wstring(yNow) + L"\n";
 			//OutputDebugString(str.c_str());
 
-			auto pointColor = colorLerp(pt1.m_color, pt2.m_color, (float)index / sumStep);
+			RGBA pointColor;
+
+			if (m_enableTexture && m_texture != nullptr) {
+				// 开启贴图 并且贴图有效 使用贴图颜色
+				floatV2 uv = uvLerp(pt1.m_uv, pt2.m_uv, (float)index / sumStep);
+				pointColor = m_texture->GetColorByUV(uv);
+			}
+			else {
+				pointColor = colorLerp(pt1.m_color, pt2.m_color, (float)index / sumStep);
+			}
 			drawPoint(Point(xNow, yNow, pointColor));
 			if (p >= 0) {
 				if (useXStep) {
@@ -161,6 +170,7 @@ namespace GT {
 		}
 		float s = (float)(npt.m_y - ptMin.m_y) / (float)(ptMax.m_y - ptMin.m_y);
 		npt.m_color = colorLerp(ptMin.m_color, ptMax.m_color, s);
+		npt.m_uv = uvLerp(ptMin.m_uv, ptMax.m_uv, s);
 
 		drawTriangleFlat(ptMid, npt, ptMax);
 		drawTriangleFlat(ptMid, npt, ptMin);
@@ -183,6 +193,16 @@ namespace GT {
 		m_UseBlend = inUseBlend;
 	}
 
+	void Canvas::enableTexture(bool inEnable)
+	{
+		m_enableTexture = inEnable;
+	}
+
+	void Canvas::bindTexture(Image* inImage)
+	{
+		m_texture = inImage;
+	}
+
 	void Canvas::drawTriangleFlat(const Point& pt1, const Point& pt2, const Point& pt)
 	{
 		float k1 = 0.0f;
@@ -213,18 +233,27 @@ namespace GT {
 
 		for (; step <= totalStemp; posY += stepValue, ++step) {
 			int l1x = startX + 1 / k1 * stepValue * step;
-			RGBA color1 = colorLerp(pt.m_color, pt1.m_color, step * 1.0 / totalStemp);
+			float scale = step * 1.0f / totalStemp;
+			RGBA color1 = colorLerp(pt.m_color, pt1.m_color, scale);
+			floatV2 uv1 = uvLerp(pt.m_uv, pt1.m_uv, scale);
 			int l2x = startX + 1 / k2 * stepValue * step;
-			RGBA color2 = colorLerp(pt.m_color, pt2.m_color, step * 1.0 / totalStemp);
+			RGBA color2 = colorLerp(pt.m_color, pt2.m_color, scale);
+			floatV2 uv2 = uvLerp(pt.m_uv, pt2.m_uv, scale);
 
 			int edgeX1 = UTool::clamp(l1x, 0, m_Width);	// 将边界限制在画布左右两边 避免出现 x = -100000 或者 x = 100000 的情况
 			int edgeX2 = UTool::clamp(l2x, 0, m_Width);
 
-			RGBA edgeColor1 = colorLerp(color1, color2, abs(edgeX1 - l1x) * 1.0f / abs(l2x - l1x));	// 根据端点颜色 重新计算直线两端颜色
-			RGBA edgeColor2 = colorLerp(color1, color2, abs(edgeX2 - l1x) * 1.0f / abs(l2x - l1x));
+			float pointScale1 = abs(edgeX1 - l1x) * 1.0f / abs(l2x - l1x);	// 插值计算 X1 端点的颜色
+			float pointScale2 = abs(edgeX2 - l1x) * 1.0f / abs(l2x - l1x);	// 插值计算 X2 端点的颜色
+
+			RGBA edgeColor1 = colorLerp(color1, color2, pointScale1);	// 根据端点颜色 重新计算直线两端颜色
+			floatV2 edgeUV1 = uvLerp(uv1, uv2, pointScale1);
+
+			RGBA edgeColor2 = colorLerp(color1, color2, pointScale2);
+			floatV2 edgeUV2 = uvLerp(uv1, uv2, pointScale2);
 
-			Point p1 = Point(edgeX1, posY, edgeColor1);
-			Point p2 = Point(edgeX2, posY, edgeColor2);
+			Point p1 = Point(edgeX1, posY, edgeColor1, edgeUV1);
+			Point p2 = Point(edgeX2, posY, edgeColor2, edgeUV2);
 
 			drawLine(p1, p2);
 		}
@@ -297,6 +326,15 @@ namespace GT {
 
 		return result;
 	}
+	
+	inline floatV2 Canvas::uvLerp(const floatV2 inUV1, floatV2 inUV2, float inScale)
+	{
+		floatV2 result;
+		result.x = inUV1.x + (inUV2.x - inUV1.x) * inScale;
+		result.y = inUV1.y + (inUV2.y - inUV1.y) * inScale;
+		return result;
+	}
+
 	void Canvas::drawImage(int inX, int inY, GT::Image* inImage)
 	{
 		for (int u = 0; u < inImage->GetWidth(); ++u) {

+ 9 - 0
OpenGL/src/WindowsProjectTest/WindowsProjectTest/Canvas.h

@@ -17,6 +17,8 @@ namespace GT {
 		byte m_alphaLimit{ 0 }; // 大于此值的像素才可以进行绘制
 		bool m_UseBlend = true; // 是否进行颜色混合
 
+		bool m_enableTexture = true;	// 是否启用纹理贴图
+		Image* m_texture{ nullptr };	// 绘制剩下顶点的时候 使用那种图片
 	public: 
 		Canvas(int _width, int _height, void* _buffer) {
 			if (_width <= 0 || _height <= 0) {
@@ -44,6 +46,9 @@ namespace GT {
 		// 颜色线性插值
 		inline RGBA colorLerp(const RGBA& _color1, const RGBA& _color2, float _scale);
 
+		// uv 插值
+		inline floatV2 uvLerp(const floatV2 inUV1, floatV2 inUV2, float inScale);
+
 		// 
 		void drawImage(int inX, int inY, GT::Image* inImage);
 
@@ -62,6 +67,10 @@ namespace GT {
 
 		void setBlend(bool inUseBlend);
 
+		void enableTexture(bool inEnable);
+
+		void bindTexture(Image* inImage);
+
 	protected:
 		// 平底平顶三角形绘制 pt1 和 pt2 是平底或平顶的两点,pt 是单独的一个顶点
 		void drawTriangleFlat(const Point& pt1, const Point& pt2, const Point& pt);

+ 3 - 1
OpenGL/src/WindowsProjectTest/WindowsProjectTest/GTMATH.hpp

@@ -84,11 +84,13 @@ namespace GT
 		int m_x;
 		int m_y;
 		RGBA m_color;
-		Point(int _x = 0, int _y = 0, RGBA _color = RGBA(0, 0, 0, 0))
+		floatV2 m_uv;
+		Point(int _x = 0, int _y = 0, RGBA _color = RGBA(0, 0, 0, 0), floatV2 _uv = floatV2(0.0f, 0.0f))
 		{
 			m_x = _x;
 			m_y = _y;
 			m_color = _color;
+			m_uv = _uv;
 		}
 		~Point()
 		{

+ 7 - 0
OpenGL/src/WindowsProjectTest/WindowsProjectTest/Image.cpp

@@ -17,6 +17,13 @@ namespace GT {
 		return m_Data[y * m_Width + x];
 	}
 
+	RGBA Image::GetColorByUV(floatV2 inUV) const
+	{
+		int x = inUV.x * GetWidth();
+		int y = inUV.y * GetHeight();
+		return GetColor(x, y);
+	}
+
 	Image* Image::readFromFile(const char* _fileName)
 	{
 		int			_picType = 0;

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


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

@@ -57,8 +57,19 @@ void Render() {
     //    GT::Point(wWidth, 0, GT::RGBA(0, 255, 0, 0)), 
     //    GT::Point(wWidth / 2, wHeight, GT::RGBA(0, 0, 255, 0)));
 
-    _canvas->drawImage(0, 0, _bgImage);
-    _canvas->drawImage(200, 200, _image);
+    // 测试图片
+    //_canvas->drawImage(0, 0, _bgImage);
+    //_canvas->drawImage(200, 200, _image);
+
+    // 测试贴图
+    _canvas->bindTexture(_bgImage);
+    _canvas->enableTexture(true);
+
+	_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))
+    );
 
 	// 将 hMem 的数据一次写入到 hDC 中
 	BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
@@ -122,7 +133,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
     delete _image;
     _image = zoomImage;
 
-    _bgImage = GT::Image::readFromFile("res/carma.png");
+    _bgImage = GT::Image::readFromFile("res/bk.jpg");
 	//_zoomImage = GT::Image::zoomImage(_image, 3, 3);
 	//_zoomImageSimple = GT::Image::zoomImageSimple(_image, 3, 3);
 	//// _image->setAlpha(0.5);