卡通人物的贴图及阴影实现。
在抗锯齿方面,OpenGL中只需glEnable(GL_LINE_SMOOTH)。
对于一个向量的旋转,直接乘以旋转矩阵可以得出。下面为代码,
void RotateVector(MATRIX &M, VECTOR &V, VECTOR &D) // Rotate a vector {D.X = M.Data[0] * V.X + M.Data[4] * V.Y + M.Data[8] * V.Z; // Rotate round the x axisD.Y = M.Data[1] * V.X + M.Data[5] * V.Y + M.Data[9] * V.Z; // Rotate round the y axisD.Z = M.Data[2] * V.X + M.Data[6] * V.Y + M.Data[10] * V.Z; // Rotate round the z axis }
灯光设置为Z轴正方向(0,0,1),然后计算其法向量normal,接着再和旋转后的向量来个点积得到shade值。若小于零,就设置为零。这个shade值可以作为一维贴图坐标。
对于边框线条,首先得开启glEnable(GL_BLEND),然后设置blend mode即glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA),接着glPolygonMode(GL_BACK, GL_LINE)画反面多边形,最后是深度缓存glDepthFunc(GL_LEQUAL)。
下面为代码,
#ifndef GL_FRAMEWORK_INCLUDED #define GL_FRAMEWORK_INCLUDED#include <windows.h>typedef struct { // Structure for keyboard stuffBOOL keyDown[256]; } Keys;typedef struct { // Contains information vital to applications HMODULE hInstance; // Application Instanceconst char* className; } Application;typedef struct { // Window creation infoApplication* application;char* title;int width;int height;int bitsPerPixel;BOOL isFullScreen; } GL_WindowInit;typedef struct { // Contains information vital to a windowKeys* keys;HWND hWnd; // Windows handleHDC hDC; // Device contextHGLRC hRC; // Rendering context GL_WindowInit init;BOOL isVisible; // Window visiable?DWORD lastTickCount; // Tick counter } GL_Window;void TerminateApplication(GL_Window* window); // Terminate the applicationvoid ToggleFullscreen(GL_Window* window); // Toggle fullscreen / Windowed mode BOOL Initialize(GL_Window* window, Keys* keys);void Deinitialize(void);void Update(DWORD milliseconds);void Draw(void);#endif
#include <Windows.h> #include <GL\glew.h> #include <GL\glut.h> #include "Previous.h"#define WM_TOGGLEFULLSCREEN (WM_USER+1) // Application define message for toggling // between fulscreen / windowed mode static BOOL g_isProgramLooping; // Window creation loop, for fullscreen / windowed mode static BOOL g_createFullScreen; // If true, then create windowvoid TerminateApplication(GL_Window* window) // Terminate the application {PostMessage(window->hWnd, WM_QUIT, 0, 0); // Send a WM_QUIT messageg_isProgramLooping = FALSE; // Stop looping of the program }void ToggleFullscreen(GL_Window* window) // Toggle fullscreen /windowed mode {PostMessage(window->hWnd, WM_TOGGLEFULLSCREEN, 0, 0); // Send a WM_TOGGLEFULLSCREEN message }void ReshapeGL(int width, int height) // Reshape the window when it's moved or resized {glViewport(0, 0, (GLsizei)(width), (GLsizei)(height)); // Reset the current viewport glMatrixMode(GL_PROJECTION);glLoadIdentity();// Calcutate the aspect ratio of the windowgluPerspective(45.0f, (GLfloat)(width) / (GLfloat)(height), 1.0, 100.0f);glMatrixMode(GL_MODELVIEW);glLoadIdentity(); }BOOL ChangeScreenResolution(int width, int height, int bitsPerPixel) // Change the screen resolution {DEVMODE dmScreenSettings; // Device modeZeroMemory(&dmScreenSettings, sizeof(DEVMODE)); // Make sure memory is cleareddmScreenSettings.dmSize = sizeof(DEVMODE); // Size of the devmode structuredmScreenSettings.dmPelsWidth = width;dmScreenSettings.dmPelsHeight = height;dmScreenSettings.dmBitsPerPel = bitsPerPixel;dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {return FALSE; // Display change failed }return TRUE; }BOOL CreateWindowGL(GL_Window* window) {DWORD windowStyle = WS_OVERLAPPEDWINDOW; // Define window styleDWORD windowExtendedStyle = WS_EX_APPWINDOW; // Define the window's extended style PIXELFORMATDESCRIPTOR pdf = {sizeof(PIXELFORMATDESCRIPTOR), // Size of this pixel format descriptor1, // Version NumberPFD_DRAW_TO_WINDOW | // Format must support windowPFD_SUPPORT_OPENGL | // Format must support openGLPFD_DOUBLEBUFFER, // Must support double bufferingPFD_TYPE_RGBA, // Request an RGBA formatwindow->init.bitsPerPixel, // Select color depth0, 0, 0, 0, 0, 0, // Color bits ignored0, // No alpha buffer0, // Shift bit ignored0, // No accumulation buffer0, 0, 0, 0, // Accumulation bits ignored16, // 16bits Z-buffer (depth buffer)0, // No stencil buffer0, // No auxiliary bufferPFD_MAIN_PLANE, // Main drawing layer0, // Reserved0, 0, 0 // Layer masks ignored };RECT windowRect = { 0, 0, window->init.width, window->init.height }; // Window coordiantes GLuint PixelFormat;if (window->init.isFullScreen == TRUE) {if (ChangeScreenResolution(window->init.width, window->init.height, window->init.bitsPerPixel) == FALSE){// Fullscreen mode failed, run in windowed mode insteadMessageBox(HWND_DESKTOP, "Mode Switch Failed.\nRuning In Windowed Mode.","Error", MB_OK | MB_ICONEXCLAMATION);window->init.isFullScreen = FALSE;}else {ShowCursor(FALSE);windowStyle = WS_POPUP; // Popup windowwindowExtendedStyle |= WS_EX_TOPMOST;}}else {// Adjust window, account for window bordersAdjustWindowRectEx(&windowRect, windowStyle, 0, windowExtendedStyle);}// Create Opengl windowwindow->hWnd = CreateWindowEx(windowExtendedStyle, // Extended stylewindow->init.application->className, // Class namewindow->init.title, // Window titlewindowStyle, // Window style0, 0, // Window X,Y positionwindowRect.right - windowRect.left, // Window widthwindowRect.bottom - windowRect.top, // Window heightHWND_DESKTOP, // Desktop is window's parent0, // No menuwindow->init.application->hInstance, // Pass the window instance window);if (window->hWnd == 0) { // Was window creation a success?return FALSE;}window->hDC = GetDC(window->hWnd);if (window->hDC == 0) {DestroyWindow(window->hWnd);window->hWnd = 0;return FALSE;}PixelFormat = ChoosePixelFormat(window->hDC, &pdf); // Find a compatible pixel formatif (PixelFormat == 0) {ReleaseDC(window->hWnd, window->hDC); // Release device contextwindow->hDC = 0;DestroyWindow(window->hWnd);window->hWnd = 0;return FALSE;}if (SetPixelFormat(window->hDC, PixelFormat, &pdf) == FALSE) { // Try to set the pixel formatReleaseDC(window->hWnd, window->hDC);window->hDC = 0;DestroyWindow(window->hWnd);window->hWnd = 0;return FALSE;}window->hRC = wglCreateContext(window->hDC); // Try to get a rendering contextif (window->hRC == 0) {ReleaseDC(window->hWnd, window->hDC);window->hDC = 0;DestroyWindow(window->hWnd);window->hWnd = 0;return FALSE;}// Make the rendering context our current rendering contextif (wglMakeCurrent(window->hDC, window->hRC) == FALSE) {wglDeleteContext(window->hRC); // Delete the rendering contextwindow->hRC = 0;ReleaseDC(window->hWnd, window->hDC);window->hDC = 0;DestroyWindow(window->hWnd);window->hWnd = 0;return FALSE;}ShowWindow(window->hWnd, SW_NORMAL); // Make the window visiablewindow->isVisible = TRUE;ReshapeGL(window->init.width, window->init.height); // Reshape our GL windowZeroMemory(window->keys, sizeof(Keys)); // Clear all keyswindow->lastTickCount = GetTickCount();return TRUE; }BOOL DestoryWindowGL(GL_Window* window) {if (window->hWnd != 0) {if (window->hDC != 0) {wglMakeCurrent(window->hDC, 0); // Setting current active rendering context to zeroif (window->hRC != 0) {wglDeleteContext(window->hRC);window->hRC = 0;}ReleaseDC(window->hWnd, window->hDC);window->hDC = 0;}DestroyWindow(window->hWnd);window->hWnd = 0;}if (window->init.isFullScreen) {ChangeDisplaySettings(NULL, 0); // Switch back to desktop resolution ShowCursor(TRUE);}return TRUE; }// Process window message callback LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {// Get the window contextGL_Window* window = (GL_Window*)(GetWindowLong(hWnd, GWL_USERDATA));switch (uMsg) { // Evaluate window messagecase WM_SYSCOMMAND: // Intercept system commands {switch (wParam) { // Check system callscase SC_SCREENSAVE: // Screensaver trying to start?case SC_MONITORPOWER: // Mointer trying to enter powersave?return 0; // Prevent form happening }break;}return 0;case WM_CREATE:{CREATESTRUCT* creation = (CREATESTRUCT*)(lParam); // Store window structure pointerwindow = (GL_Window*)(creation->lpCreateParams);SetWindowLong(hWnd, GWL_USERDATA, (LONG)(window));}return 0;case WM_CLOSE:TerminateApplication(window);return 0;case WM_SIZE:switch (wParam) {case SIZE_MINIMIZED: // Was window minimized?window->isVisible = FALSE;return 0;case SIZE_MAXIMIZED:window->isVisible = TRUE;ReshapeGL(LOWORD(lParam), HIWORD(lParam));return 0;case SIZE_RESTORED:window->isVisible = TRUE;ReshapeGL(LOWORD(lParam), HIWORD(lParam));return 0;}break;case WM_KEYDOWN:if ((wParam >= 0) && (wParam <= 255)) {window->keys->keyDown[wParam] = TRUE; // Set the selected key(wParam) to truereturn 0;}break;case WM_KEYUP:if ((wParam >= 0) && (wParam <= 255)) {window->keys->keyDown[wParam] = FALSE;return 0;}break;case WM_TOGGLEFULLSCREEN:g_createFullScreen = (g_createFullScreen == TRUE) ? FALSE : TRUE;PostMessage(hWnd, WM_QUIT, 0, 0);break;}return DefWindowProc(hWnd, uMsg, wParam, lParam); // Pass unhandle message to DefWindowProc }BOOL RegisterWindowClass(Application* application) {WNDCLASSEX windowClass;ZeroMemory(&windowClass, sizeof(WNDCLASSEX)); // Make sure memory is clearedwindowClass.cbSize = sizeof(WNDCLASSEX); // Size of the windowClass structurewindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraws the window for any movement / resizingwindowClass.lpfnWndProc = (WNDPROC)(WindowProc); // WindowProc handles messagewindowClass.hInstance = application->hInstance; // Set the instancewindowClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE);// Class background brush colorwindowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Load the arrow pointerwindowClass.lpszClassName = application->className; // Sets the application classNameif (RegisterClassEx(&windowClass) == 0) {MessageBox(HWND_DESKTOP, "RegisterClassEx Failed!", "Error", MB_OK | MB_ICONEXCLAMATION);return FALSE;}return TRUE; }int WINAPI WinMain(HINSTANCE hIstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {Application application;GL_Window window;Keys keys;BOOL isMessagePumpActive;MSG msg;DWORD tickCount;application.className = "OpenGL";application.hInstance = hIstance;ZeroMemory(&window, sizeof(GL_Window));window.keys = &keys; // Window key structurewindow.init.application = &application; // Window applicationwindow.init.title = "Cel-Shading"; // Window titlewindow.init.width = 640; // Window widthwindow.init.height = 480; // Window heightwindow.init.bitsPerPixel = 16; // Bits per pixelwindow.init.isFullScreen = TRUE; // Fullscreen? (set to TRUE) ZeroMemory(&keys, sizeof(Keys));if (MessageBox(HWND_DESKTOP, "Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO | MB_ICONQUESTION) == IDNO){window.init.isFullScreen = FALSE;}if (RegisterWindowClass(&application) == FALSE){MessageBox(HWND_DESKTOP, "Error Registering Window Class!", "Error", MB_OK | MB_ICONEXCLAMATION);return -1;}g_isProgramLooping = TRUE;g_createFullScreen = window.init.isFullScreen;while (g_isProgramLooping) { // Loop until WM_QUIT is receivedwindow.init.isFullScreen = g_createFullScreen; // Set init param of window creation to fullscreen?if (CreateWindowGL(&window) == TRUE) { // Was window creation successful?// At this point we should have a window that is setup to render OpenGLif (Initialize(&window, &keys) == FALSE) {TerminateApplication(&window); // Close window, this will handle the shutdown }else {isMessagePumpActive = TRUE;while (isMessagePumpActive == TRUE) {// Success creating window. Check for window messagesif (PeekMessage(&msg, window.hWnd, 0, 0, PM_REMOVE) != 0) {if (msg.message != WM_QUIT) {DispatchMessage(&msg);}else {isMessagePumpActive = FALSE; // Terminate the message pump }}else {if (window.isVisible == FALSE) {WaitMessage(); // Application is minimized wait for a message }else {// Process application looptickCount = GetTickCount(); // Get the tick countUpdate(tickCount - window.lastTickCount); // Update the counterwindow.lastTickCount = tickCount;// Set last count to current countDraw(); // Draw screen SwapBuffers(window.hDC);}}}}// Application is finished Deinitialize();DestoryWindowGL(&window);}else {MessageBox(HWND_DESKTOP, "Error Creating OpenGL Window", "Error", MB_OK | MB_ICONEXCLAMATION);g_isProgramLooping = FALSE;}}UnregisterClass(application.className, application.hInstance); // UnRegister window classreturn 0; }
#include <Windows.h> #include <GL/glew.h> #include <GL/glut.h> #include <GL/GLUAX.H> #include <math.h> #include <stdio.h> #include "Previous.h"#pragma comment(lib, "legacy_stdio_definitions.lib")#ifndef CDS_FULLSCREEN #define CDS_FULLSCREEN 4 #endifGL_Window* g_window; Keys* g_keys;typedef struct tagMATRIX {float Data[16]; // Matrix format } MATRIX;typedef struct tagVECTOR {float X, Y, Z; } VECTOR;typedef struct tagVERTEX {VECTOR Nor; // NormalVECTOR Pos; // Position } VERTEX;typedef struct tagPOLYGON { // PolygonVERTEX Verts[3]; } POLYGON;bool outlineDraw = true; // Flag to draw the outline bool outlineSmooth = false; // Flag to Anti-Alias the lines float outlineColor[3] = { 0.0f, 0.0f, 0.0f }; // Color of the line float outlineWidth = 3.0f; // Width of the lines VECTOR lightAngle; // The direction of the light bool lightRotate = false; // Flag to see if rotate the lightfloat modelAngle = 0.0f; // Y-axis angle of the model bool modelRotate = false; // Flag to rotate the model POLYGON* polyData = NULL; // Polygon data int polyNum = 0; // Number of polygon GLuint shaderTexture[1]; // Storage for one texture BOOL ReadMesh() {FILE* In = fopen("Data/model.txt", "rb");if (!In) {return FALSE;}fread(&polyNum, sizeof(int), 1, In); // Read the header (number of polygon)polyData = (POLYGON*)malloc(sizeof(POLYGON) * polyNum);fread(&polyData[0], sizeof(POLYGON) * polyNum, 1, In);fclose(In);return TRUE; }inline float DotProduct(VECTOR &V1, VECTOR &V2) {return V1.X * V2.X + V1.Y * V2.Y + V1.Z * V2.Z; }inline float Magnitude(VECTOR &V) // The length of the vector {return sqrt(V.X * V.X + V.Y * V.Y + V.Z * V.Z); }void Normalize(VECTOR &V) // Create a vector with a unit length of 1 {float M = Magnitude(V);if (M != 0.0f) {V.X /= M;V.Y /= M;V.Z /= M;} }void RotateVector(MATRIX &M, VECTOR &V, VECTOR &D) // Rotate a vector {D.X = M.Data[0] * V.X + M.Data[4] * V.Y + M.Data[8] * V.Z; // Rotate round the x axisD.Y = M.Data[1] * V.X + M.Data[5] * V.Y + M.Data[9] * V.Z; // Rotate round the y axisD.Z = M.Data[2] * V.X + M.Data[6] * V.Y + M.Data[10] * V.Z; // Rotate round the z axis }BOOL Initialize(GL_Window* window, Keys* keys) {char Line[255];float shaderData[32][3];FILE *In = NULL;g_window = window;g_keys = keys;glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Realy nice perspective calculationsglClearColor(0.1f, 0.1f, 0.1f, 0.0f);glClearDepth(1.0f); glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LESS);glShadeModel(GL_SMOOTH);glDisable(GL_LINE_SMOOTH);glEnable(GL_CULL_FACE);glDisable(GL_LIGHTING);In = fopen("Data/shader.txt", "r");if (In) {for (int i = 0; i < 32; i++) {if (feof(In)) break;fgets(Line, 255, In);shaderData[i][0] = shaderData[i][1] = shaderData[i][2] = float(atof(Line));}fclose(In);}else {return FALSE;}glGenTextures(1, &shaderTexture[0]);glBindTexture(GL_TEXTURE_1D, shaderTexture[0]);glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 32, 0, GL_RGB, GL_FLOAT, shaderData);lightAngle.X = 0.0f;lightAngle.Y = 0.0f;lightAngle.Z = 1.0f;Normalize(lightAngle);return ReadMesh(); }void Deinitialize(void) {glDeleteTextures(1, &shaderTexture[0]);free(polyData); }void Update(DWORD milliseconds) {if (g_keys->keyDown[' '] == TRUE) {modelRotate = !modelRotate; // Toggle model rotationg_keys->keyDown[' '] = FALSE;}if (g_keys->keyDown['1'] == TRUE) {outlineDraw = !outlineDraw; // Tpggle outline drawingg_keys->keyDown['1'] = FALSE;}if (g_keys->keyDown['2'] == TRUE) {outlineSmooth = !outlineSmooth; // Toggle Anti-Aliasingg_keys->keyDown['2'] = FALSE;}if (g_keys->keyDown[VK_UP] == TRUE) {outlineWidth++; // Line widthg_keys->keyDown[VK_UP] = FALSE;}if (g_keys->keyDown[VK_DOWN] == TRUE) {outlineWidth--; // Line widthg_keys->keyDown[VK_DOWN] = FALSE;}if (modelRotate) {modelAngle += (float)(milliseconds) / 10.0f;} }void Draw() {float TmpShade; // Temporary shader value MATRIX TmpMatrix;VECTOR TmpVector, TmpNormal;glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glLoadIdentity();if (outlineSmooth) { // Check glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);glEnable(GL_LINE_SMOOTH); // Enable Anti-Aliasing }else {glDisable(GL_LINE_SMOOTH);}glTranslatef(0.0f, 0.0f, -2.0f);glRotatef(modelAngle, 0.0f, 1.0f, 0.0f);glGetFloatv(GL_MODELVIEW_MATRIX, TmpMatrix.Data); // Get the generatef matrix// Cel-shading code glEnable(GL_TEXTURE_1D);glBindTexture(GL_TEXTURE_1D, shaderTexture[0]);glColor3f(0.8f, 0.2f, 0.2f);glBegin(GL_TRIANGLES);for (int i = 0; i < polyNum; ++i) {for (int j = 0; j < 3; ++j) {TmpNormal.X = polyData[i].Verts[j].Nor.X;TmpNormal.Y = polyData[i].Verts[j].Nor.Y;TmpNormal.Z = polyData[i].Verts[j].Nor.Z;RotateVector(TmpMatrix, TmpNormal, TmpVector); // Rotate Normalize(TmpVector);TmpShade = DotProduct(TmpVector, lightAngle);if (TmpShade < 0.0f) {TmpShade = 0.0f;}glTexCoord1f(TmpShade); // Set the texture coordinate as the shade valueglVertex3fv(&polyData[i].Verts[j].Pos.X);}}glEnd();glDisable(GL_TEXTURE_1D);if (outlineDraw) { // Check glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Blend mode glPolygonMode(GL_BACK, GL_LINE); // Draw backfacing polygon as wireframes glLineWidth(outlineWidth);glCullFace(GL_FRONT); // Don't draw any front-face polygon glDepthFunc(GL_LEQUAL);glColor3fv(&outlineColor[0]); // Set the outline color glBegin(GL_TRIANGLES);for (int i = 0; i < polyNum; ++i) {for (int j = 0; j < 3; ++j) {glVertex3fv(&polyData[i].Verts[j].Pos.X);}}glEnd();glDepthFunc(GL_LESS); // ResetglCullFace(GL_BACK); // ResetglPolygonMode(GL_BACK, GL_FILL); // Reset glDisable(GL_BLEND);} }
Thanks for Nehe's tutorials, this is his home.