首页 > 代码库 > outdated: 37.Cel-Shading
outdated: 37.Cel-Shading
卡通人物的贴图及阴影实现。
在抗锯齿方面,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 axis D.Y = M.Data[1] * V.X + M.Data[5] * V.Y + M.Data[9] * V.Z; // Rotate round the y axis D.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 stuff BOOL keyDown[256];} Keys;typedef struct { // Contains information vital to applications HMODULE hInstance; // Application Instance const char* className;} Application;typedef struct { // Window creation info Application* application; char* title; int width; int height; int bitsPerPixel; BOOL isFullScreen;} GL_WindowInit;typedef struct { // Contains information vital to a window Keys* keys; HWND hWnd; // Windows handle HDC hDC; // Device context HGLRC 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 modeBOOL 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 modestatic BOOL g_isProgramLooping; // Window creation loop, for fullscreen / windowed modestatic 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 message g_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 window gluPerspective(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 mode ZeroMemory(&dmScreenSettings, sizeof(DEVMODE)); // Make sure memory is cleared dmScreenSettings.dmSize = sizeof(DEVMODE); // Size of the devmode structure dmScreenSettings.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 style DWORD windowExtendedStyle = WS_EX_APPWINDOW; // Define the window‘s extended style PIXELFORMATDESCRIPTOR pdf = { sizeof(PIXELFORMATDESCRIPTOR), // Size of this pixel format descriptor 1, // Version Number PFD_DRAW_TO_WINDOW | // Format must support window PFD_SUPPORT_OPENGL | // Format must support openGL PFD_DOUBLEBUFFER, // Must support double buffering PFD_TYPE_RGBA, // Request an RGBA format window->init.bitsPerPixel, // Select color depth 0, 0, 0, 0, 0, 0, // Color bits ignored 0, // No alpha buffer 0, // Shift bit ignored 0, // No accumulation buffer 0, 0, 0, 0, // Accumulation bits ignored 16, // 16bits Z-buffer (depth buffer) 0, // No stencil buffer 0, // No auxiliary buffer PFD_MAIN_PLANE, // Main drawing layer 0, // Reserved 0, 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 instead MessageBox(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 window windowExtendedStyle |= WS_EX_TOPMOST; } } else { // Adjust window, account for window borders AdjustWindowRectEx(&windowRect, windowStyle, 0, windowExtendedStyle); } // Create Opengl window window->hWnd = CreateWindowEx(windowExtendedStyle, // Extended style window->init.application->className, // Class name window->init.title, // Window title windowStyle, // Window style 0, 0, // Window X,Y position windowRect.right - windowRect.left, // Window width windowRect.bottom - windowRect.top, // Window height HWND_DESKTOP, // Desktop is window‘s parent 0, // No menu window->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 format if (PixelFormat == 0) { ReleaseDC(window->hWnd, window->hDC); // Release device context window->hDC = 0; DestroyWindow(window->hWnd); window->hWnd = 0; return FALSE; } if (SetPixelFormat(window->hDC, PixelFormat, &pdf) == FALSE) { // Try to set the pixel format ReleaseDC(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 context if (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 context if (wglMakeCurrent(window->hDC, window->hRC) == FALSE) { wglDeleteContext(window->hRC); // Delete the rendering context window->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 visiable window->isVisible = TRUE; ReshapeGL(window->init.width, window->init.height); // Reshape our GL window ZeroMemory(window->keys, sizeof(Keys)); // Clear all keys window->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 zero if (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 callbackLRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ // Get the window context GL_Window* window = (GL_Window*)(GetWindowLong(hWnd, GWL_USERDATA)); switch (uMsg) { // Evaluate window message case WM_SYSCOMMAND: // Intercept system commands { switch (wParam) { // Check system calls case 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 pointer window = (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 true return 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 cleared windowClass.cbSize = sizeof(WNDCLASSEX); // Size of the windowClass structure windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraws the window for any movement / resizing windowClass.lpfnWndProc = (WNDPROC)(WindowProc); // WindowProc handles message windowClass.hInstance = application->hInstance; // Set the instance windowClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE);// Class background brush color windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Load the arrow pointer windowClass.lpszClassName = application->className; // Sets the application className if (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 structure window.init.application = &application; // Window application window.init.title = "Cel-Shading"; // Window title window.init.width = 640; // Window width window.init.height = 480; // Window height window.init.bitsPerPixel = 16; // Bits per pixel window.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 received window.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 OpenGL if (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 messages if (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 loop tickCount = GetTickCount(); // Get the tick count Update(tickCount - window.lastTickCount); // Update the counter window.lastTickCount = tickCount;// Set last count to current count Draw(); // 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 class return 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; // Normal VECTOR Pos; // Position} VERTEX;typedef struct tagPOLYGON { // Polygon VERTEX Verts[3];} POLYGON;bool outlineDraw = true; // Flag to draw the outlinebool outlineSmooth = false; // Flag to Anti-Alias the linesfloat outlineColor[3] = { 0.0f, 0.0f, 0.0f }; // Color of the linefloat outlineWidth = 3.0f; // Width of the linesVECTOR lightAngle; // The direction of the lightbool lightRotate = false; // Flag to see if rotate the lightfloat modelAngle = 0.0f; // Y-axis angle of the modelbool modelRotate = false; // Flag to rotate the modelPOLYGON* polyData = http://www.mamicode.com/NULL; // Polygon dataint polyNum = 0; // Number of polygonGLuint shaderTexture[1]; // Storage for one textureBOOL 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 = http://www.mamicode.com/(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 axis D.Y = M.Data[1] * V.X + M.Data[5] * V.Y + M.Data[9] * V.Z; // Rotate round the y axis D.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 calculations glClearColor(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 rotation g_keys->keyDown[‘ ‘] = FALSE; } if (g_keys->keyDown[‘1‘] == TRUE) { outlineDraw = !outlineDraw; // Tpggle outline drawing g_keys->keyDown[‘1‘] = FALSE; } if (g_keys->keyDown[‘2‘] == TRUE) { outlineSmooth = !outlineSmooth; // Toggle Anti-Aliasing g_keys->keyDown[‘2‘] = FALSE; } if (g_keys->keyDown[VK_UP] == TRUE) { outlineWidth++; // Line width g_keys->keyDown[VK_UP] = FALSE; } if (g_keys->keyDown[VK_DOWN] == TRUE) { outlineWidth--; // Line width g_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 value glVertex3fv(&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); // Reset glCullFace(GL_BACK); // Reset glPolygonMode(GL_BACK, GL_FILL); // Reset glDisable(GL_BLEND); }}
Thanks for Nehe‘s tutorials, this is his home.
outdated: 37.Cel-Shading
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。