[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系統的 Anaconda(python環境)搭建
題記——初聽不識曲中意,再聽已是曲中人。 序曲 一直以來想找個機會與時間去了解一下機器學習。與此同時,吳恩達博士的名字一直在耳邊迴響,卻不知為何如此響徹。後來,在couresa上看到了吳恩達博士的《機器學習》課程,才將機器學習與吳恩達博士聯絡在了一起。之後,瞭解了課程在機器學習領域具有里程碑
基於OpenCV3實現人臉識別(原理篇)---PCA(Principal 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(