как повернуть картинку на 90 градусов влево: #define  NOMINMAX
#include <windows.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <cmath>
#include <chrono>
#include <limits>
#include "PngImage.h"
using namespace std;
const int w = 800, h = 800;
uint32_t image[w][h];
const int depth = 255;
template <class t> struct Vec2 {
	t x, y;
	Vec2<t>() : x(t()), y(t()) {}
	Vec2<t>(t _x, t _y) : x(_x), y(_y) {}
	Vec2<t>(const Vec2<t>& v) : x(t()), y(t()) { *this = v; }
	Vec2<t>& operator =(const Vec2<t>& v) {
		if (this != &v) {
			x = v.x;
			y = v.y;
		}
		return *this;
	}
	Vec2<t> operator +(const Vec2<t>& V) const { return Vec2<t>(x + V.x, y + V.y); }
	Vec2<t> operator -(const Vec2<t>& V) const { return Vec2<t>(x - V.x, y - V.y); }
	Vec2<t> operator *(float f)          const { return Vec2<t>(x * f, y * f); }
	t& operator[](const int i) { return i == 0 ? x : y; }
	template <class > friend ostream& operator<<(ostream& s, Vec2<t>& v);
};
template <class t> struct Vec3 {
	t x, y, z;
	Vec3<t>() : x(t()), y(t()), z(t()) {}
	Vec3<t>(t _x, t _y, t _z) : x(_x), y(_y), z(_z) {}
	Vec3<t>(const Vec3<t>& v) : x(t()), y(t()), z(t()) { *this = v; }
	template <class u> Vec3(const Vec3<u>& v);
	Vec3<t>& operator =(const Vec3<t>& v) {
		if (this != &v) {
			x = v.x;
			y = v.y;
			z = v.z;
		}
		return *this;
	}
	Vec3<t> operator ^(const Vec3<t>& v) const {
		return Vec3<t>(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
	}
	Vec3<t> operator +(const Vec3<t>& v) const { return Vec3<t>(x + v.x, y + v.y, z + v.z); }
	Vec3<t> operator -(const Vec3<t>& v) const { return Vec3<t>(x - v.x, y - v.y, z - v.z); }
	Vec3<t> operator *(float f)          const { return Vec3<t>(x * f, y * f, z * f); }
	t       operator *(const Vec3<t>& v) const { return x * v.x + y * v.y + z * v.z; }
	float norm() const { return sqrt(x * x + y * y + z * z); }
	Vec3<t>& normalize(t l = 1) { *this = (*this) * (l / norm()); return *this; }
	t& operator[](const int i) { if (i == 0) return x; else if (i == 1) return y; else return z; }
	template <class > friend ostream& operator<<(ostream& s, Vec3<t>& v);
};
template <class t>
template <class u>
Vec3<t>::Vec3(const Vec3<u>& v)
	: x(static_cast<t>(v.x)), y(static_cast<t>(v.y)), z(static_cast<t>(v.z)) {
}
typedef Vec2<float> Vec2f;
typedef Vec2<int>   Vec2i;
typedef Vec3<float> Vec3f;
typedef Vec3<int>   Vec3i;
template <class t> ostream& operator<<(ostream& s, Vec2<t>& v) {
	s << "(" << v.x << ", " << v.y << ")";
	return s;
}
template <class t> ostream& operator<<(ostream& s, Vec3<t>& v) {
	s << "(" << v.x << ", " << v.y << ", " << v.z << ")";
	return s;
}
class Model
{
private:
	vector<Vec3f> verts_;
	vector<vector<Vec3i>> faces_;
	vector<Vec3f> norms_;
	vector<Vec2f> uv_;
	png::PngImage diffusemap_;
	void load_texture(const char* filename) {
		diffusemap_.read(filename);
		diffusemap_.flipVertically();
	}
public:
	Model(const string& buffer, const char* diffuseTexName) : verts_(), faces_() {
		istringstream in(buffer);
		string line;
		while (getline(in, line)) {
			istringstream iss(line);
			char trash;
			if (!line.compare(0, 2, "v ")) {
				iss >> trash;
				Vec3f v;
				for (int i = 0; i < 3; i++) iss >> v[i];
				verts_.push_back(v);
			}
			else if (!line.compare(0, 3, "vn ")) {
				iss >> trash >> trash;
				Vec3f n;
				for (int i = 0; i < 3; i++) iss >> n[i];
				norms_.push_back(n);
			}
			else if (!line.compare(0, 3, "vt ")) {
				iss >> trash >> trash;
				Vec2f uv;
				for (int i = 0; i < 2; i++) iss >> uv[i];
				uv_.push_back(uv);
			}
			else if (!line.compare(0, 2, "f ")) {
				vector<Vec3i> f;
				Vec3i tmp;
				iss >> trash;
				while (iss >> tmp[0] >> trash >> tmp[1] >> trash >> tmp[2]) {
					for (int i = 0; i < 3; i++) tmp[i]--;
					f.push_back(tmp);
				}
				faces_.push_back(f);
			}
		}
		load_texture(diffuseTexName);
	}
	~Model() {
	}
	int nverts() const {
		return (int)verts_.size();
	}
	int nfaces() const {
		return (int)faces_.size();
	}
	Vec2f uv(int iface, int nvert) {
		int idx = faces_[iface][nvert][1];
		return uv_[idx];
	}
	Vec3f normal(int iface, int nvert) {
		int idx = faces_[iface][nvert][2];
		return norms_[idx];
	}
	uint32_t diffuse(Vec2f uvf) {
		return diffusemap_.getPixel(
			uvf.x * (diffusemap_.getImage().width - 1),
			uvf.y * (diffusemap_.getImage().height - 1));
	}
	vector<int> face(int idx) {
		vector<int> face;
		for (int i = 0; i < (int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]);
		return face;
	}
	Vec3f vert(int i) const {
		return verts_[i];
	}
};
void triangle(Vec3f* pts, Vec2f* uvs, Vec3f* norms, uint32_t(&image)[w][h], int* zbuffer, Vec3f light_dir, Model* model) {
	Vec3i t0(int((pts[0].x + 1.) * w / 2.), int((pts[0].y + 1.) * h / 2.), int((pts[0].z + 1.) * depth / 2.));
	Vec3i t1(int((pts[1].x + 1.) * w / 2.), int((pts[1].y + 1.) * h / 2.), int((pts[1].z + 1.) * depth / 2.));
	Vec3i t2(int((pts[2].x + 1.) * w / 2.), int((pts[2].y + 1.) * h / 2.), int((pts[2].z + 1.) * depth / 2.));
	if (t0.y == t1.y && t0.y == t2.y) return;
	if (t0.y > t1.y) { swap(t0, t1); swap(uvs[0], uvs[1]); swap(norms[0], norms[1]); }
	if (t0.y > t2.y) { swap(t0, t2); swap(uvs[0], uvs[2]); swap(norms[0], norms[2]); }
	if (t1.y > t2.y) { swap(t1, t2); swap(uvs[1], uvs[2]); swap(norms[1], norms[2]); }
	int total_height = t2.y - t0.y;
	for (int i = 0; i <= total_height; i++) {
		bool second_half = i > t1.y - t0.y || t1.y == t0.y;
		int segment_height = second_half ? t2.y - t1.y : t1.y - t0.y;
		float alpha = total_height == 0 ? 0. : (float)i / total_height;
		float beta = segment_height == 0 ? 0. : (float)(i - (second_half ? t1.y - t0.y : 0)) / segment_height;
		Vec3i A = t0 + Vec3i(t2 - t0) * alpha;
		Vec3i B = second_half ? t1 + Vec3i(t2 - t1) * beta : t0 + Vec3i(t1 - t0) * beta;
		Vec2f uvA = uvs[0] + (uvs[2] - uvs[0]) * alpha;
		Vec2f uvB = second_half ? uvs[1] + (uvs[2] - uvs[1]) * beta : uvs[0] + (uvs[1] - uvs[0]) * beta;
		Vec3f nA = norms[0] + (norms[2] - norms[0]) * alpha;
		Vec3f nB = second_half ? norms[1] + (norms[2] - norms[1]) * beta : norms[0] + (norms[1] - norms[0]) * beta;
		if (A.x > B.x) { swap(A, B); swap(uvA, uvB); swap(nA, nB); }
		for (int j = A.x; j <= B.x; j++) {
			float phi = B.x == A.x ? 1.0f : (float)(j - A.x) / (float)(B.x - A.x);
			Vec3i P = A + Vec3i(B - A) * phi;
			Vec2f uvP = uvA + (uvB - uvA) * phi;
			Vec3f nP = nA + (nB - nA) * phi;
			P.x = j; P.y = t0.y + i;
			int idx = P.x + P.y * w;
			if (P.x >= w || P.y >= h || P.x < 0 || P.y < 0) continue;
			if (zbuffer[idx] < P.z) {
				zbuffer[idx] = P.z;
				nP.normalize();
				float intensity = max(0.f, nP * light_dir);
				uint32_t packed = model->diffuse(uvP);

				image[P.x][P.y] =
					(uint8_t(packed) & 0xFF << 24) |  // A
					(uint8_t(((packed >> 24) & 0xFF) * intensity) << 16) |  // R
					(uint8_t(((packed >> 16) & 0xFF) * intensity) << 8) |  // G
					(uint8_t(((packed >> 8) & 0xFF) * intensity) << 0);      // B
			}
		}
	}
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
	int* zbuffer = new int[w * h];
	for (int i = 0; i < w * h; i++) {
		zbuffer[i] = numeric_limits<int>::min();
	}
	Vec3f light_dir(0, 0, 1);
	ifstream in("african_head.obj");
	string buffer((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
	in.close();
	Model* model = new Model(buffer, "african_head_diffuse.png");
	for (int i = 0; i < model->nfaces(); i++) {
		Vec3f pts[3];
		Vec2f uvs[3];
		Vec3f norms[3];
		for (int j = 0; j < 3; j++) {
			int idx = model->face(i)[j];
			pts[j] = model->vert(idx);
			uvs[j] = model->uv(i, j);
			norms[j] = model->normal(i, j);
			norms[j].normalize();
		}
		triangle(pts, uvs, norms, image, zbuffer, light_dir, model);
	}
	WNDCLASS wc = {};
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.lpszClassName = L"SimpleWindowClass";
	RegisterClass(&wc);
	HWND hwnd = CreateWindowEx(
		0,
		L"SimpleWindowClass",
		L"Image",
		WS_MINIMIZEBOX | WS_SYSMENU,
		CW_USEDEFAULT, CW_USEDEFAULT, w, h,
		NULL,
		NULL,
		hInstance,
		NULL
	);
	if (hwnd == NULL) {
		return 0;
	}
	ShowWindow(hwnd, nCmdShow);
	MSG msg = {};
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	delete model;
	delete[] zbuffer;
	return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	switch (uMsg) {
	case WM_PAINT: {
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hwnd, &ps);
		BITMAPINFO bmi = {};
		bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bmi.bmiHeader.biWidth = w;
		bmi.bmiHeader.biHeight = -h;
		bmi.bmiHeader.biPlanes = 1;
		bmi.bmiHeader.biBitCount = 32;
		bmi.bmiHeader.biCompression = BI_RGB;
		StretchDIBits(
			hdc,
			0, 0, w, h,
			0, 0, w, h,
			image,
			&bmi,
			DIB_RGB_COLORS,
			SRCCOPY
		);
		EndPaint(hwnd, &ps);
	}
	return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}