1. 程式人生 > >[OpenGL] 基於紋理繪製的透明精靈(透明紋理)

[OpenGL] 基於紋理繪製的透明精靈(透明紋理)

        在二維遊戲中,我們幾乎繞不開精靈繪製這一過程,除了直接在opengl讀入影象並繪製外,我們更常使用紋理來完成這一過程,把紋理貼到在xy平面上的面片,做出二維遊戲的效果。

        這樣我們可以很方便的使用opengl提供給我們的一些方法來執行精靈的變換,而不是使用大量的貼圖來手工完成變換過程;同時,還可以通過調節深度資訊來確定物體的遮擋關係,而不用花心思考慮繪製的先後順序,因為我們知道,在二維世界裡,誰遮擋誰是和繪製順序相關的。

        但是,我們也發現,我們的精靈不總是四四方方的,所以紋理載入進來後會有背景色,我們希望去除這個背景色,一個非常好的想法就是使用帶透明(alpha)通道的圖片,可是非常遺憾的是,opengl不能直接支援這種格式,所以這個方法不可行。

        還有一種比較常見的想法是這樣的:我們使用一種顏色代表背景色,在opengl中開啟alpha測試,把紋理圖片設定為rgba格式,讀取到這種顏色後,我們把alpha手動設為0,也就是全透明;否則設為1,也就是不透明。我們可以意識到:這個背景色的選取是有要求的,它不能與圖片不透明部分rgb相同,否則它們會被錯誤識別為透明的。

        以下是我最近正在寫的一個基於opengl的小遊戲,這裡使用了透明紋理,其中火箭是網上暫時找的圖片,用於測試效果,下面的飛船圖片是自己做的。由於火箭沒有進行特殊處理,所以我們發現它的中間部分也有被鏤空的,這就是本次程式碼存在的一些缺陷,它需要我們對圖片進行特殊的處理才能使用。

        上圖的程式碼會在完成後放出,為了演示透明紋理效果,我又單獨給出了一個測試程式碼:

        原圖(圖1來自網路):

       

        使用透明紋理後效果:

       

        在這個程式碼中,背景色是通過引數指定的。

        需要注意的是,在繪製的時候,我們儘可能先繪製一般紋理的面片,然後統一繪製透明紋理的面片,在繪製透明紋理時,需要把深度測試設定為只讀模式,最後再修改回原來的狀態,防止在進行處理的時候破壞了深度資訊。

test.h

#pragma once
#define GLUT_DISABLE_ATEXIT_HACK  
#include "GL/GLUT.H"  
void loadTex(int i, char *filename, GLuint* texture);//一般紋理
void loadTex(int i, char *filename, GLuint* texture,unsigned char* backgroundColor);//透明紋理

texture.cpp
#define _CRT_SECURE_NO_WARNINGS  
#include<stdio.h>  
#include<windows.h>  
#include"test.h"  
#define BITMAP_ID 0x4D42   


//讀紋理圖片    
static unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{

    FILE *filePtr;    // 檔案指標    
    BITMAPFILEHEADER bitmapFileHeader;    // bitmap檔案頭    
    unsigned char    *bitmapImage;        // bitmap影象資料    
    int    imageIdx = 0;        // 影象位置索引    
    unsigned char    tempRGB;    // 交換變數    

                                 // 以“二進位制+讀”模式開啟檔案filename     
    filePtr = fopen(filename, "rb");
    if (filePtr == NULL) {
        printf("file not open\n");
        return NULL;
    }
    // 讀入bitmap檔案圖    
    fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
    // 驗證是否為bitmap檔案    
    if (bitmapFileHeader.bfType != BITMAP_ID) {
        fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n");
        return NULL;
    }
    // 讀入bitmap資訊頭    
    fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
    // 將檔案指標移至bitmap資料    
    fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
    // 為裝載影象資料建立足夠的記憶體    
    bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage];
    // 驗證記憶體是否建立成功    
    if (!bitmapImage) {
        fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
        return NULL;
    }

    // 讀入bitmap影象資料    
    fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);
    // 確認讀入成功    
    if (bitmapImage == NULL) {
        fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
        return NULL;
    }
    //由於bitmap中儲存的格式是BGR,下面交換R和B的值,得到RGB格式    
    for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) {
        tempRGB = bitmapImage[imageIdx];
        bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
        bitmapImage[imageIdx + 2] = tempRGB;
    }
    // 關閉bitmap影象檔案   
    fclose(filePtr);
    return bitmapImage;
}

//讀紋理圖片    
static unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader, unsigned char* backgroundColor)
{

    FILE *filePtr;    // 檔案指標    
    BITMAPFILEHEADER bitmapFileHeader;    // bitmap檔案頭    
    unsigned char    *bitmapImage;        // bitmap影象資料    
    int    imageIdx = 0;        // 影象位置索引    

                                // 以“二進位制+讀”模式開啟檔案filename     
    filePtr = fopen(filename, "rb");
    if (filePtr == NULL) {
        printf("file not open\n");
        return NULL;
    }
    // 讀入bitmap檔案圖    
    fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
    // 驗證是否為bitmap檔案    
    if (bitmapFileHeader.bfType != BITMAP_ID) {
        fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n");
        return NULL;
    }
    // 讀入bitmap資訊頭    
    fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
    // 將檔案指標移至bitmap資料    
    fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
    // 為裝載影象資料建立足夠的記憶體    
    bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage];
    // 驗證記憶體是否建立成功    
    if (!bitmapImage) {
        fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
        return NULL;
    }

    // 讀入bitmap影象資料    
    fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);
    // 確認讀入成功    
    if (bitmapImage == NULL) {
        fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
        return NULL;
    }
    unsigned char*   bitmapData;   // 紋理資料 

    bitmapData = new unsigned char[bitmapInfoHeader->biSizeImage / 3 * 4];

    int count = 0;
    //新增alpha通道
    for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) {
        bitmapData[count] = bitmapImage[imageIdx + 2];
        bitmapData[count + 1] = bitmapImage[imageIdx + 1];
        bitmapData[count + 2] = bitmapImage[imageIdx];
        if (bitmapData[count] == backgroundColor[0]
            && bitmapData[count + 1] == backgroundColor[1] 
            && bitmapData[count + 2] == backgroundColor[2]){
            bitmapData[count + 3] = 0;
        }
        else bitmapData[count + 3] = 255;
        count += 4;
    }

    // 關閉bitmap影象檔案   
    fclose(filePtr);
    return bitmapData;
}

//載入紋理的函式    
void loadTex(int i, char *filename, GLuint* texture)
{

    BITMAPINFOHEADER bitmapInfoHeader;                                 // bitmap資訊頭    
    unsigned char*   bitmapData;                                       // 紋理資料    

    bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader);

    glBindTexture(GL_TEXTURE_2D, texture[i]);
    // 指定當前紋理的放大/縮小過濾方式    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    glTexImage2D(GL_TEXTURE_2D,
        0,         //mipmap層次(通常為,表示最上層)     
        GL_RGB,    //我們希望該紋理有紅、綠、藍資料    
        bitmapInfoHeader.biWidth, //紋理寬頻,必須是n,若有邊框+2     
        bitmapInfoHeader.biHeight, //紋理高度,必須是n,若有邊框+2     
        0, //邊框(0=無邊框, 1=有邊框)     
        GL_RGB,    //bitmap資料的格式    
        GL_UNSIGNED_BYTE, //每個顏色資料的型別    
        bitmapData);    //bitmap資料指標    

}

//載入紋理的函式    
void loadTex(int i, char *filename, GLuint* texture, unsigned char* backgroundColor)
{

    BITMAPINFOHEADER bitmapInfoHeader;                                 // bitmap資訊頭    
    unsigned char*   bitmapData;                                       // 紋理資料    

    bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader,backgroundColor);

    glBindTexture(GL_TEXTURE_2D, texture[i]);
    // 指定當前紋理的放大/縮小過濾方式    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    glTexImage2D(GL_TEXTURE_2D,
        0,         //mipmap層次(通常為,表示最上層)     
        GL_RGBA,    //我們希望該紋理有紅、綠、藍、alpha資料    
        bitmapInfoHeader.biWidth, //紋理寬頻,必須是n,若有邊框+2     
        bitmapInfoHeader.biHeight, //紋理高度,必須是n,若有邊框+2     
        0, //邊框(0=無邊框, 1=有邊框)     
        GL_RGBA,    //bitmap資料的格式    
        GL_UNSIGNED_BYTE, //每個顏色資料的型別    
        bitmapData);    //bitmap資料指標    

}

main.cpp
#define _CRT_SECURE_NO_WARNINGS  

#include <stdio.h>  
#include <string.h>  
#include<time.h>
#include <stdlib.h>
#include"test.h"  

  
#include <math.h>       /* for cos(), sin(), and sqrt() */    
GLuint texture[2];
//視區  
float whRatio;
int wHeight = 0;
int wWidth = 0;


//視點  
float center[] = { 0, 0, 0 };
float eye[] = { 0, 0, 5 };


void drawRect(GLuint texture)
{
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texture);  //選擇紋理texture[status]   

	const GLfloat x1 = -0.5, x2 = 0.5;
	const GLfloat y1 = -0.5, y2 = 0.5;
	const GLfloat point[4][2] = { { x1,y1 },{ x2,y1 },{ x2,y2 },{ x1,y2 } };
	int dir[4][2] = { { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } };
	glBegin(GL_QUADS);

	for (int i = 0; i < 4; i++) {
		glTexCoord2iv(dir[i]);
		glVertex2fv(point[i]);
	}
	glEnd();

	glDisable(GL_TEXTURE_2D);
}


void drawScene()
{
	glPushMatrix();
	glScalef(8, 6, 1);
	drawRect(texture[0]);
	glPopMatrix();

	glDepthMask(GL_FALSE);

	glPushMatrix();
	glTranslatef(0.0f,-1.0f, 1.0f);
	glScalef(1.8, 2, 0);
	drawRect(texture[1]);
	glPopMatrix();

	glDepthMask(GL_TRUE);
}

void updateView(int height, int width)
{
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);//設定矩陣模式為投影       
	glLoadIdentity();   //初始化矩陣為單位矩陣          
	whRatio = (GLfloat)width / (GLfloat)height;  //設定顯示比例     
	glOrtho(-3, 3, -3, 3, -100, 100); //正投影
	glMatrixMode(GL_MODELVIEW);  //設定矩陣模式為模型    
}

void reshape(int width, int height)
{
	if (height == 0)      //如果高度為0      
	{
		height = 1;   //讓高度為1(避免出現分母為0的現象)      
	}

	wHeight = height;
	wWidth = width;

	updateView(wHeight, wWidth); //更新視角      
}


void idle()
{
	glutPostRedisplay();
}



void init()
{
	srand(unsigned(time(NULL)));
	glEnable(GL_DEPTH_TEST);//開啟深度測試   
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER, 0.5);
	glEnable(GL_LIGHTING);  //開啟光照模式   

	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);


	unsigned char color[] = { 255,255,255 };
	glGenTextures(2, texture);
	loadTex(0, "1.bmp", texture);
	loadTex(1, "2.bmp", texture,color);

}



void redraw()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除顏色和深度快取    
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();   //初始化矩陣為單位矩陣      
	gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], 0, 1, 0);                // 場景(0,0,0)的視點中心 (0,5,50),Y軸向上  
	glPolygonMode(GL_FRONT, GL_FILL);

	glFrontFace(GL_CCW);
	glEnable(GL_CULL_FACE);
	// 啟用光照計算
	glEnable(GL_LIGHTING);
	// 指定環境光強度(RGBA)
	GLfloat ambientLight[] = { 2.0f, 2.0f, 2.0f, 1.0f };

	// 設定光照模型,將ambientLight所指定的RGBA強度值應用到環境光
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
	// 啟用顏色追蹤
	glEnable(GL_COLOR_MATERIAL);
	// 設定多邊形正面的環境光和散射光材料屬性,追蹤glColor
	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

	drawScene();//繪製場景   
	glutSwapBuffers();//交換緩衝區  
}

int main(int argc, char *argv[])
{

	glutInit(&argc, argv);//對glut的初始化         
	glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
	//初始化顯示模式:RGB顏色模型,深度測試,雙緩衝           
	glutInitWindowSize(800, 600);//設定視窗大小         
	int windowHandle = glutCreateWindow("Simple GLUT App");//設定視窗標題           
	glutDisplayFunc(redraw); //註冊繪製回撥函式         
	glutReshapeFunc(reshape);   //註冊重繪回撥函式         

	glutIdleFunc(idle);//註冊全域性回撥函式:空閒時呼叫       

	init();
	glutMainLoop();  // glut事件處理迴圈       
	return 0;
}


相關推薦

[OpenGL] 基於紋理繪製透明精靈透明紋理

        在二維遊戲中,我們幾乎繞不開精靈繪製這一過程,除了直接在opengl讀入影象並繪製外,我們更常使用紋理來完成這一過程,把紋理貼到在xy平面上的面片,做出二維遊戲的效果。         這樣我們可以很方便的使用opengl提供給我們的一些方法來執行精靈的變換

CentOS 7 配置squid 透明代理測試筆記

一:計劃進行的方式, 1.系統安裝的是centos 7 min。 2.squid是直接yum install squid.service安裝的。 3.因為對firewall-cmd不瞭解,所以先按照從前方式使用iptables安裝完成以後,在使用firewall-cmd來操

聚類:層次聚類、基於劃分的聚類k-means基於密度的聚類、基於模型的聚類

oca 基本思想 初始化 methods 根據 範圍 下使用 對象 適用於 一、層次聚類 1、層次聚類的原理及分類 1)層次法(Hierarchicalmethods)先計算樣本之間的距離。每次將距離最近的點合並到同一個類。然後,再計算類與類之間的距離,將距離最近的類合

2018.4.28 基於java的聊天系統帶完善

group false com opera listen nec xtend imp 選擇 Java聊天系統 1.Socket類 Socket(InetAddress address, int port) 創建一個流套接字並將其連接到指定 IP 地址的指定端口號。 S

基於Skyline的web開發6.x

返回 lin 加載 code 場景 ner explorer 對象 設置 多窗口對比 一個頁面加載多個TerraExplorer3DWindow和SGWorld等只有第一個能用(即使用iframe也是一樣) 所以我決定打開兩個新頁面實現多窗口對比,然後我在《主頁面》使用wi

基於深度學習的VQA視覺問答技術

mark一下,感謝作者分享! http://www.sohu.com/a/225043785_99992181 https://blog.csdn.net/sinat_26917383/article/details/73048045 https://blog.csdn.net/A

5.4基於範圍的for迴圈c++11之讀書筆記

基於範圍的for迴圈,簡化了迴圈了任務:對陣列(或者容器,如vector和array)的每個元素執行相同的操作,如下例所示: double prices[5]={4.99,10.99,6.87,7.99,8.49}; for (double x:prices) cout<

資料結構實現 4.1:集合_基於二分搜尋樹實現C++版

資料結構實現 4.1:集合_基於二分搜尋樹實現(C++版) 1. 概念及基本框架 2. 基本操作程式實現 2.1 增加操作 2.2 刪除操作 2.3 查詢操作 2.4 其他操作 3. 演算法複雜度分析

基於Pytorch實現風格遷移CS231n assignment3

       風格遷移由Gatys等與2015年提出,論文:https://www.cv-foundation.org/openaccess/content_cvpr_2016/papers/Gatys_Image_Style_Transfer_CVPR_20

基於堆的優先佇列Java實現

優先佇列的最重要的操作:刪除最大元素(或最小)和插入元素。資料結構二叉堆能夠很好的實現佇列的基本操作。 二叉堆的結點按照層級順序放入陣列,用長度為N+1的私有陣列pq來表示一個大小為N的堆(堆元素放在pq[1]至pq[N]之間,為方便計數,未使用pq[0]),跟

影象分割—基於圖的影象分割Graph-BasedImageSegmentation

影象分割—基於圖的影象分割(Graph-Based Image Segmentation) Reference: Efficient Graph-Based Image Segmentation,IJCV 2004,MIT Code Graph-Based Se

ESP8266-天貓精靈智慧家居

1:使用器材 天貓精靈、esp8266、LED(繼電器) (本文所使用的是ESP8266-12-F系列) 2:使用平臺 貝殼物聯 註冊自己的賬號,增加相應的裝置 3:天貓精靈新增智慧家居裝置 1:選擇裝置位置:(臥室、廚房。。。) 2:選擇

Spring框架的事務管理之基於AspectJ的XML方式重點掌握

1. 步驟一:恢復轉賬開發環境(轉賬開發環境見“https://www.cnblogs.com/wyhluckdog/p/10137283.html”) 2.步驟二:引入AOP的開發包3.步驟三:引入applicationContext.xml配置檔案  * 配置檔案的基本配置為: <?xml

2.基於redis非同步佇列模組Reactor模式-執行緒池還是Redis還是Rabbitmq訊息佇列作為非同步處理的選擇

1.訊息佇列和多執行緒兩者並不衝突,多執行緒可以作為佇列的生產者和消費者。使用外部的訊息佇列時,第一是可以提高應用的穩定性,當程式fail後,寫入外部訊息佇列的資料依舊是儲存的,如果使用兩步commit的佇列的話,可以更加提高這個專案。2. 用執行緒的話,會佔用主伺服器資源,

學習—吳恩達《機器學習》—手敲程式碼_準備工作之基於Ubuntu系統的 Anacondapython環境搭建

題記——初聽不識曲中意,再聽已是曲中人。 序曲 一直以來想找個機會與時間去了解一下機器學習。與此同時,吳恩達博士的名字一直在耳邊迴響,卻不知為何如此響徹。後來,在couresa上看到了吳恩達博士的《機器學習》課程,才將機器學習與吳恩達博士聯絡在了一起。之後,瞭解了課程在機器學習領域具有里程碑

基於OpenCV3實現人臉識別原理篇---PCAPrincipal Component Analysis

實踐總結:   1首先了解做人臉識別的步驟   2各個演算法後面的原理   3原理背後的相關知識的瞭解   4人臉識別專案總遇到的問題                                                                  

基於內容的推薦演算法推薦系統

距離上次更新已經不知道有多久了,因為過幾日就是中期答辯了,為了不太監開始堅持把這個專案往後做一做。 這次我們要做的是什麼呢,要先搭建整個開發環境,目前用到的如下:mysql,idea,IKAnalyzer2012_u6(一個開源的分詞包,完全夠用了) 這次我計劃先完成最簡單

最簡單的基於FFmpeg的AVDevice例子讀取攝像頭解讀

本文轉載自最簡單的基於FFmpeg的AVDevice例子(讀取攝像頭) 在此基礎上對程式的流程進行解讀,閱讀前請先閱讀原文。 ============================= /** * 最簡單的基於FFmpeg的AVDevice例子(讀取攝像頭) * Simplest F

openshift/origin學習記錄1——基於二進位制檔案的安裝單機版

先決條件 開啟SELINUX 官方文件推薦開啟SELINUX,否則會導致安裝失敗。 修改/etc/selinux/config SELINUX=enforcing SELINUXTYPE=targeted 安裝docker # y

基於內容的推薦演算法推薦系統

因為要報賬,趕著做出來一個用來展示的網站,用來申請軟體著作權然後拿到發票趕緊報銷去。所以用了幾個小時的時間弄出來一個醜不拉幾的網站,還好之前web作業做過一部分。現在的話是這樣弄得: 整體架構如下用了IDEA開發,基於Java EE,tomcat和MySQL(