=================================
=================================
=================================
출처: http://apecoder.net/2010/11/17/using-2d-opengl-on-android-part-2/
The earlier OpenGL on Android tutorial was about setting up the GLSurfaceView. Obviously this is important as what will you be drawing to if you don’t have a surface! So I recommend you read it before you continue. Remember I am only aiming to produce 2D!
draw_texture
In this tutorial we will render our first texture. You may be thinking “wait a minute, don’t you need to explain polygons, vertices and indices?”. Nope! Thankfully OpenGL has a useful function ‘draw_texture’ which to you and me (OpenGL ES) isglDrawTexfOES. This function will draw our texture straight to the surface. We need to make sure draw_texture is supported, to do this just add the following to your renderer class’ onSurfaceCreated function.
...
// Test for device specific extensions
String extensions = gl10.glGetString(GL10.GL_EXTENSIONS);
// Test for draw texture boolean drawTexture = extensions.contains("draw_texture");
Log.i("OpenGL Support - ver.:" + gl10.glGetString(GL10.GL_VERSION) + "renderer:" +
gl10.glGetString(GL10.GL_RENDERER) + " : " + (drawTexture ? "good to go!" : "forget it!!"));
You should probably show an error dialog or something here, but for now we’ll just write to the log that there is a problem.
Setting up a texture
Ok so that worked fine, right? Now we can load our .png file and setup the texture. The following code needs to be added to the onSurfaceCreadted method of our Renderer. Looking at this code you can see that the first thing we do is get a texture name, which is essentially a number that OpenGL uses to index all the different textures. Now that we have a texture name, we switch to this texture with the function glBindTexture.
Whenever we need to swap to a different texture, this is the function we’ll need. There are two types of texture target (the first parameter to this funciton): GL_TEXTURE_2D and GL_TEXTURE_1D. 2D textures are your common image file, and 1D textures are a flat array of pixels. Maybe I’ll explain these in another tutorial. Or if you need to know now checkout the tutorials over at nehe.gamedev.net for info on this.
The crop array basically defines a square, which we can ignore for now (this will be explained in a future tutorial). I have put this in the setup code to simplify the example. Now for what is usually the most tricky bit! Luckily for us the nice Android developers have given us a handy OpenGL utility function that takes our bitmap stream and sets up the internal 2D texture. Thanks guys.
...
mTextureName = new int[1];
// Generate Texture ID
gl10.glGenTextures(1, mTextureName, 0);
// Did generate work? assert gl10.glGetError() == GL10.GL_NO_ERROR;
// Bind texture id texturing target (we want 2D of course)
gl10.glBindTexture(GL10.GL_TEXTURE_2D, mTextureName[0]);
// Open and input stream and read the image. InputStream is = mContext.getResources().openRawResource(R.drawable.asteroid);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// Build our crop texture to be the size of the bitmap (ie full texture)
mCrop = new int[4];
mCrop[0] = 0;
mCrop[1] = imageHeight = bitmap.getHeight();
mCrop[2] = imageWidth = bitmap.getWidth();
mCrop[3] = -bitmap.getHeight();
// Magic Android function that setups up the internal formatting
// of the bitmap for OpenGL ES
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Did texImage work?
assert gl10.glGetError() == GL10.GL_NO_ERROR;
bitmap.recycle();
Draw the texture
Ok, now the fun part, drawing our texture. The following needs to be added to our onDrawFrame function. I’m not going to explain the functions below, this is where you need to experiment and find out what this does. Enjoy!
...
// Begin drawing
//--------------
// These function calls can be experimented with for various effects such
// as transparency although certain functionality maybe device specific.
gl.glShadeModel(GL10.GL_FLAT);
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
// Setup correct projection matrix gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glPushMatrix();
gl.glLoadIdentity();
gl.glOrthof(0.0f, mWidth, 0.0f, mHeight, 0.0f, 1.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glPushMatrix(); gl.glLoadIdentity();
gl.glEnable(GL10.GL_TEXTURE_2D);
// Draw Textures
//----------------
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureName[0]);
((GL11)gl).glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, mCrop, 0);
((GL11Ext)gl).glDrawTexfOES(0, 0, 0, imageWidth, imageHeight);
// Draw the texture a few more times
((GL11Ext)gl).glDrawTexfOES(100, 100, 0, imageWidth, imageHeight);
((GL11Ext)gl).glDrawTexfOES(250, 200, 0, imageWidth, imageHeight);
// Finish drawing (reset projection matrix)
//------------------
gl.glDisable(GL10.GL_BLEND);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glPopMatrix();
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glPopMatrix();
The next OpenGL tutorial will be about using buffers for extra speedy drawing!
Download source
=================================
=================================
=================================
OpenGL ES는 3차원 공간입니다. 하지만, 2D 좌표계처럼 사용하고 싶을 때가 있습니다.
그럴 때는 당연히 x, y, z 좌표중 z 좌표를 0으로 세팅하고 사용을 하겠죠.
하지만, 좌표만 바꾼다고 단말기 스크린(Screen)의 픽셀 좌표 1개가 OpenGL 상의 원하는 좌표가
되지 않을 경우가 많습니다.
이 때는 ViewPort를 적절하게 잘 조절을 해줘야 합니다.
그러기 위해서는 다음과 같이 세팅하면 됩니다.
gl.glOrthof(0, 480, 0, 800, 1, -1);
이 때 값들은 480x800 해상도를 가진 단말기 기준입니다.
그런데, 이 경우는 y좌표가 단말기 위쪽으로 갈수록 커지고 단말기 아래쪽이 0이 됩니다.
우리가 흔히 사용하는 GUI 좌표에서는 y좌표가 위쪽이 0이고, 아래로 갈수록 커지죠.
즉, 이 때는 이렇게 할 수 있습니다.
gl.glOrthof(0, 480, 800, 0, 1, -1);
자, 그러면 이 픽셀 좌표에 맞게 삼각형을 그리는 전체 소스를 한 번 보겠습니다.
SnowOpenGLESActivity.java
package snowdeer.opengles.test;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class SnowOpenGLESActivity extends Activity
{
private GLSurfaceView m_gsView = null;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
m_gsView = new GLSurfaceView(this);
m_gsView.setRenderer(new SnowRenderer());
setContentView(m_gsView);
}
@Override
protected void onPause()
{
super.onPause();
m_gsView.onPause();
}
@Override
protected void onResume()
{
super.onResume();
m_gsView.onResume();
}
}
SnowRenderer.java
package snowdeer.opengles.test;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLSurfaceView.Renderer;
public class SnowRenderer implements Renderer
{
private SnowTriangle m_SnowTriangle = null;
public SnowRenderer()
{
m_SnowTriangle = new SnowTriangle();
}
public void onDrawFrame(GL10 gl)
{
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity(); // Matrix 리셋
gl.glTranslatef(0.0 f, 0.0 f, 0.0 f);
m_SnowTriangle.draw(gl);
}
public void onSurfaceChanged(GL10 gl, int width, int height)
{
if (height == 0)
{
height = 1; // 0으로 나누는 것을 방지하기 위해서
}
gl.glViewport(0, 0, width, height); // ViewPort 리셋
gl.glMatrixMode(GL10.GL_PROJECTION); // MatrixMode를 Project Mode로
gl.glLoadIdentity(); // Matrix 리셋
// 윈도우의 Aspect Ratio 설정
gl.glOrthof(0, 480, 800, 0, 1, -1);
gl.glMatrixMode(GL10.GL_MODELVIEW); // Matrix를 ModelView Mode로 변환
gl.glLoadIdentity(); // Matrix 리셋
}
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
gl.glShadeModel(GL10.GL_SMOOTH); // Smooth Shading이 가능하도록 설정
gl.glClearColor(0.0 f, 0.0 f, 0.0 f, 1.0 f); // 하얀 바탕 그리기
gl.glClearDepthf(1.0 f); // Depth Buffer 세팅
gl.glEnable(GL10.GL_DEPTH_TEST); // Depth Test 가능하도록 설정
gl.glDepthFunc(GL10.GL_LEQUAL); // The Type Of Depth Testing
// glHint 설정
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}
}
SnowTriangle.java
package snowdeer.opengles.test;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class SnowTriangle
{
private FloatBuffer m_vertexBuffer;
// 삼각형 좌표 입력
private float vertices[] = {
0.0 f,
0.0 f,
0.0 f,
240.0 f,
800.0 f,
0.0 f,
480.0 f,
0.0 f,
0.0 f,
};
public SnowTriangle()
{
ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
m_vertexBuffer = byteBuf.asFloatBuffer();
m_vertexBuffer.put(vertices);
m_vertexBuffer.position(0);
}
public void draw(GL10 gl)
{
gl.glFrontFace(GL10.GL_CW); // 시계방향 그리기 설정
// VertexPointer 설정
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, m_vertexBuffer);
// Vertex Array 사용 가능하도록 설정
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 삼각형 Strip 그리기
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
// Vertex Array 사용 상태를 다시 불가능하도록 설정
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
}
=================================
=================================
=================================
출처: http://pros2.tistory.com/85
OpenGL로 만드는 2D 비행슈팅 게임.
신입을 맞이한 3D 시그의 첫 주제는 게임 개발을 통한 OpenGL과 친해지기 입니다.
주제는 2D 슈팅게임 (1945)로써, 처음 게임을 제작하는 사람들에게 많은 예제가 되고 간편하게 접근할 수 있는 게임 이고, 게임을 개발함에 있어 필요한 노하우를 제공하고자 시그 주제로 잡아 진행하고 있습니다.
-------------------------------------------------------------------------------------------
// chapter 1. 오픈GL 셋팅하기
-------------------------------------------------------------------------------------------
#include <glut.h>
#include <gl.h>
#include <glu.h>
#include <glaux.h> //텍스쳐 파일 불러오는데 필요한 라이브러리
#pragma comment(lib, "glaux.lib")
int main(int argc, char** argv) {
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(600, 700);
glutInitWindowPosition(0, 0);
glutCreateWindow("1945 OpenGL");
glClearColor (0.0, 0.0, 0.0, 1.0);
//OpenGL 셋팅
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 60.0, 0.0, 70.0, -30.0, 30.0);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_TEXTURE_2D);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glutDisplayFunc(Render);
glutKeyboardFunc(MyKeyboard);
glutIdleFunc(FrameMove);
glutMainLoop();
return 0;
}
기본적인 OpenGL 셋팅에서 게임을 만들때 꼭 필요한 콜백 함수는 그림을 그려주는 glutDisplayFunc 과
glutIdleFunc 입니다.
DisplayFunc에서 화면에 보여지는 모든 그림들을 그리는 작업을 하고,
IdleFunc에서 위치이동, 연산 등 게임에서 돌아가는 모든 연산 작업이 이루어진다고 생각하시면 됩니다.
그래서 모든 객체들의 클래스들의 구조의 기본형태는 다음과 같습니다.
class CGame {
public:
CGame(void);
~CGame(void);
HRESULT Init();
HRESULT Render();
int FrameMove(float delta);
HRESULT Destroy();
}
게임 객체들은 초기화 과정을 생성자에서 하지 않는 이유는 캐릭터 같은경우 죽었을 때 초기화 값을 다시 넣어주기 위해서,
초기화 작업을 다시 해야하기 때문에 다시 생성자를 호출 할 수 없는 클래스 특성상 초기화 작업은 따로 메소드를 생성하여 작업하게 됩니다.
그리고 그리는 Render 메소드와 연산작업을하는 FrameMove 메소드 생성한 것을 다시 해제해 줄 수 있는 Destory를
기본형태의 클래스 구조를 가지게 됩니다.
-------------------------------------------------------------------------------------------
// chapter 2. IdleFunc에서 게임 연산
-------------------------------------------------------------------------------------------
OpenGL의 glutIdleFunc 을 이용하여 연산을 하는 이유는 타이머 콜백을 이용하여 연산을 할 경우,
각 컴퓨터들의 성능에 따라 연산량이 많아졌을 때 타이머가 제시간에 작동하지 않는 오류를 줄여 주기 위하여
타이머를 이용하지 않고 IDLE을 이용하여 연산하게 됩니다.
이때 타이머가 아니라 IDLE을 이용하여 연산을 하기 위해서는 IDLE 함수 루프에 동작시간을 구해야 합니다.
한번 돌때의 루프 시간을 구하여 그 시간을 내가 원하는 상수에 곱해주기 되면, 내가 지정한 시간에 원하는 이동
포인트 만큼을 이동시킬 수 있습니다.
//타임 관련 변수
float lastTime = (float)timeGetTime();
float timeDelta = 0.0f;
void FrameMove() //Idle
{
//루프 한번 도는데 걸리는 시간을 측정
float currTime = (float)timeGetTime();
timeDelta = (currTime - lastTime) * 0.001f;
//연산작업
//
lastTime = currTime;
}
다음 공식을 보면 이해가 조금 더 쉽게 될 수 있습니다.
게임을 설계 할때 유저 비행기의 속도를 한 루프당 + 1px 만큼 움직이겠다고 정의하는 것이 아니라.
1초에 20px을 움직일 것이다라고 초 단위의 이동 량을 정의 합니다.
이렇게 할 경우 케릭터 위치를
위치 = 20 * 루프한번도는데 걸리는 시간 ;
으로 연산하게 되면 루프한번 도는데 걸리는 시간이 0.0005초 일때
20 * 0.0005 이기 때문에 1초가 됬을 때 정확히 20만큼 움직이는 연산을 할 수 있습니다.
( 이제 부터 모든 오브젝트의 연산은 TimeDelta 값을 곱하는 방식으로 진행 되게 됩니다. )
-------------------------------------------------------------------------------------------
// chapter 3. Render Frame 제한
-------------------------------------------------------------------------------------------
다음으로 그림을 그려주는 Frame률을 제한하는 작업에 대해 설명해 드리겠습니다.
사람이 눈으로 확인 할 수 있는 프레임은 평균적으로 30 프레임 이라고 합니다. 보통 평균 화질의
영상이 30프레임률을 가지고 재생되고, 현재 3D 게임의 최고 프레임률을 60프레임정도로 제한 해서
그려지고 있습니다. 이와 같이 Render의 경우에는 사람에 눈에 보일때 무리 없을 정도만 보여주는게
성능향상에 도움이 되기 때문에 프레임률을 제한 하는 것이 좋습니다.
//프레임 제어
int timeCount = 0;
DWORD ctime;
//프레임률 제어
if(timeCount == 0)
{
ctime = timeGetTime();
timeCount++;
}
//현재시간과 처음받아온 시간을 비교하여
//60프레임 보다 크면 랜더하고 타임을 다시 받아옴
if(ctime + (1000/90) <= timeGetTime())
{
FrameCnt++;
glutPostRedisplay(); //그림을 그려주세요.
timeCount = 0;
}
위에 소스와 같이 시간을 이용하여 프레임 률을 제한 하여 Render를 호출 할 수 있습니다.
-------------------------------------------------------------------------------------------
// chapter 4. Scene 관리
-------------------------------------------------------------------------------------------
Render 함수에 모든 소스를 작성하기엔 가독성에 매우 좋지 않다.
재사용성을 위해서라도 클래스로 나누는 작업을 하기 위해 처음으로 각 Scene들을 정의한다.
게임 로딩화면, 메인화면, 선택화면, 플레잉화면, 에필로그 등 각 장면들을 각각 하나의 Scene으로 정의한다.
class CScene
{
public:
CScene(void);
virtual ~CScene(void);
virtual HRESULT Init() = 0; //게임에 필요한 생성
virtual HRESULT Render() = 0;
virtual int FrameMove(float delta) = 0;
virtual HRESULT Destroy() = 0; //게임에 필요한 소멸
};
class CGameScene :
public CScene
{
public:
CGameScene(void);
~CGameScene(void);
HRESULT Init();
HRESULT Render();
int FrameMove(float delta);
HRESULT Destroy();
};
각 해당하는 씬을 만들고 씬의 기본형을 상속받아 재정의 하여 사용 한다.
이 Scene들은 Scene 매니져 를 만들어 관리하고 사용하게 만들면 좋다. 일단 Scene을 상속 받은
GameScene을 가지고 게임을 플레이 하는 환경을 세팅 한다.
맨처음 설명했던 것과 마찬가지로 Render()와 FrameMove()를 이용하여 각 객체들을 동작하는 메소드를
각 Scene에 맞게 정의 하여 준다.
-------------------------------------------------------------------------------------------
// chapter 5. Key Input
-------------------------------------------------------------------------------------------
게임에서 키들의 메세지를 받기 위해서 여러가지 방법이 있다. Window에서 제공해주는, 콜백 함수를 이용해도
되지만 메세지 방식을 이용하게 되면, 한 메세지에서 한키의 입력값 밖에 받아 올 수 없기 때문에,
게임엔 적합하지 않다. 그래서 다음과 같이 KeyState 값을 가지고 & 연산을 이용하여 키의 눌림여부를 판별한다.
//0x8000 : 키가 눌렸다
BOOL up = ((GetAsyncKeyState(VK_UP) & 0x8000) == 0x8000);
BOOL down = ((GetAsyncKeyState(VK_DOWN) & 0x8000) == 0x8000);
BOOL bleft = ((GetAsyncKeyState(VK_LEFT) & 0x8000) == 0x8000);
BOOL bright = ((GetAsyncKeyState(VK_RIGHT) & 0x8000) == 0x8000);
다음 BOOL 변수에 키가 눌렸을 때 TRUE 값을 반환하게 하는 함수 이다.
이 BOOL 변수들로 키의 눌림 여부를 판별하여 각 키에 따른 동작을 정의하게 되면, 여러키를 동시에 처리 할 수 있다.
-------------------------------------------------------------------------------------------
// chapter 6. Render 와 Animation
-------------------------------------------------------------------------------------------
2D 게임의 기본적인 오브젝트를 그리는 방식은 사각형을 그리고 그 위에 텍스쳐를 씌워 그리는 방식을 사용한다.
다음은 OpenGL을 사용하여 오브젝트를 그리는 방법을 설명하겠다.
glEnable(GL_BLEND);
glBlendFunc(GL_DST_COLOR, GL_ZERO);
glBindTexture(GL_TEXTURE_2D, MyTextureObject);
glBegin(GL_POLYGON);
glColor3f(1.0f, 1.0f, 1.0f);
glTexCoord2f(((int)animation + 0) / 3.0f, 0.51f); glVertex3f(m_rPos.left-4.0f, m_rPos.bottom, 0.0);
glTexCoord2f(((int)animation + 1) / 3.0f, 0.51f); glVertex3f(m_rPos.right, m_rPos.bottom, 0.0);
glTexCoord2f(((int)animation + 1) / 3.0f, 0.99f); glVertex3f(m_rPos.right, m_rPos.top, 0.0);
glTexCoord2f(((int)animation + 0) / 3.0f, 0.99f); glVertex3f(m_rPos.left-4.0f, m_rPos.top, 0.0);
glEnd();
glBlendFunc(GL_ONE, GL_ONE);
glBegin(GL_POLYGON);
glColor3f(1.0f, 1.0f, 1.0f);
glTexCoord2f(((int)animation + 0) / 3.0f, 0.01f); glVertex3f(m_rPos.left-4.0f, m_rPos.bottom, 0.0);
glTexCoord2f(((int)animation + 1) / 3.0f, 0.01f); glVertex3f(m_rPos.right, m_rPos.bottom, 0.0);
glTexCoord2f(((int)animation + 1) / 3.0f, 0.49f); glVertex3f(m_rPos.right, m_rPos.top, 0.0);
glTexCoord2f(((int)animation + 0) / 3.0f, 0.49f); glVertex3f(m_rPos.left-4.0f, m_rPos.top, 0.0);
glEnd();
glDisable(GL_BLEND);
glVertex3f 를 이용 삼각형 두개를 그려 사각형을 만들고 그앞에 TexCoord를 이용하여 텍스쳐의 좌표를 지정하여
텍스쳐를 그리는 OpenGL의 전형적인 방법이다.
여기서 같은 사각형을 블랜드 옵션을 변경하여 두번 그리는 이유는 Bitmap 그림파일을 원하는 부분만 그리기 위해서는
마스크 값을 이용하여 합산하여 원하는 부위만 그리는 방식을 사용할 수 있다.
일단 이미지의 윗부분을 그려 마스크를 두고, 원본 이미지와 합산하여 원하는 부위의 이미지만을 출력할 수있다.
여기서 Texcoord의 좌표를 원하는 시간마다 변경하여 케릭터의 모습을 애니메이션 시킬 수 있다.
---------------------------------------------------------------------------------------------
=================================
=================================
=================================
'스마트기기개발관련 > OpenGL (그래픽, 게임)' 카테고리의 다른 글
애플 ios (오픈지엘 OpenGL)고정기능을 사용한 HellowArrow (0) | 2020.09.18 |
---|---|
android 안드로이드 NDK 설치해서 JNI 개발해보기 관련 (0) | 2020.09.18 |
android 안드로이드 ndk jni 오픈지엘 OpenGL ES 관련 EGL_DEFAULT_DISPLAY, eglCreateWindowSurface,glDrawTexfOES 등 에러 관련 (0) | 2020.09.18 |
android 안드로이드 NDK에서 C++ STL 사용 과련 (0) | 2020.09.18 |
안드로이드 android NDK jni 폴더안에 폴더를 만들어 빌드해보기 관련 (0) | 2020.09.18 |