몇년 전에 VisualSVN 4.x 버전에 대해 설치 방법을 알아보았는데 변경된 기능이 있는지 확인하기위해 설치 및 간단한 사용법을 알아봅니다.

한가지 주의사항은 컴퓨터 이름이 영문이어야 설치가 가능합니다.  한글인 경우 설치가 중단됩니다.

 

먼저 프로그램을 다운로드 받습니다.

 

https://www.visualsvn.com/downloads/

 

사이트에 접속하면 다양한 제품이 있는데 SVN을 찾아봅니다.

 

 

2개의 버전이 보이는데 [VisualSVN Server 5.3.1 (x64-bit)]를 클릭하고 저장할 폴더를 선택해서 VisualSVN-Server-5.3.1-x64.exe 파일을 다운로드 합니다.

 

다운로드가 완료되면 프로그램을 실행합니다.

 

 

 

제품에 Apache HTTP ServerApache Subversion이 포함되어 있다는 안내 글이 보입니다.

[Next] 버튼을 클릭합니다.

 

 

 

저작권 관련 내용으로 [I accept the terms in the License Agreement] 앞에 있는 체크박스를 클릭해야 [Next] 버튼이 활성화됩니다. [Next] 버튼을 클릭합니다.

 

 

 

컴포넌트 선택 화면인데 기본값으로 하고 [Next] 버튼을 클릭합니다.

 

 

 

설치 위치와 접속 방법 등에 대한 선택 화면입니다.

repositories 위치를 변경 하려면  [Browse...] 버튼을 클릭 하고

 

 

 

폴더를 선택해주면 됩니다.

 

 

 

Repositories 위치를 변경을 해보았습니다.

서버 접속 Port도 변경하려면 수정을 하고 [Next] 버튼을 클릭합니다.

 

 

 

설치 후 툴에서 full-text 검색이 가능하게 해준다는데 개발 툴에서 검색하면 될것 같아 [Next] 버튼을 클릭합니다.

 

 

 

사용자 인증 관리를 Subversion에서 하는 것으로 놓고 [Next] 버튼을 클릭합니다.

사용자 추가와 그룹관리는 아래에서 설명합니다.

 

 

 

설정이 완료되었습니다.  만약 수정이 필요하면 [Back] 버튼을 클릭하여 수정을 하고 설치를 진행하려면 [Install] 버튼을 클릭합니다.

 

 

 

설치가 진행됩니다.

설치하다가 컴퓨터 이름이 한글인 경우 설치가 중단됩니다. 

꼭 영문으로 컴퓨터 이름을 변경하세요.

 

 

 

설치가 완료되었습니다.  VisualSVN Server를 실행하도록 하고 [Finish] 버튼을 클릭 합니다.

 

 

 

VisualSVN Server의 모습입니다.

 

Repository를 하나 생성하도록 하겠습니다.

 

 

 

좌측 트리 메뉴에서 Repositories에 마우스를 놓고 우측 클릭을 하여 팝업 메뉴를 띄웁니다.

여기서 [Create New Repository]를 클릭합니다.

 

 

 

Regular FSFS(Fast Secure File System)으로 기본적인 저장 방법과

Distributed VDFS(VisualSVN Distributed File System)으로 master slave로 분리되어 이중으로 저장하는 방법이 있습니다.

그냥 기본으로 두고 [Next] 버튼을 클릭합니다.

 

 

 

새로 생성하는 Repository 이름을 마음대로 결정합니다.  저는 TestRepository로 했습니다.

 

 

 

Empty는 빈공간을 만들고 여기에 여러 개의 프로젝트를 추가하도록 하는 방법과 Single-project로 처음부터 하나의 프로젝트만 관리하도록 생성하는 방법이 있습니다.

기본으로 놔두고 [Next] 버튼을 클릭합니다.

 

 

 

모든 사용자가 읽기 쓰기 가능하도록 두고 [Next] 버튼을 클릭합니다.

 

 

 

앞에서 설정할때 Search Index를 체크한 경우 필요한 기능입니다.

[Create] 버튼을 클릭하여 저장소를 생성합니다.

 

 

 

저장소에 접근할 수 있는 방법으로 URL을 알려주고 있습니다.

Eclipse, Intellij등에서 형상관리를 위해 접속하는데 사용하면 됩니다.

잘 복사해 놓습니다.

 

이제 사용자와 그룹을 만들어서 관리해보도록 하겠습니다.

 

 

좌측 메뉴에서 Users에 마우스를 대고 우클릭을 합니다.

[Create User...] 메뉴를 클릭 합니다.

 

 

 

신규 사용자와 비밀번호를 입력해서 유저를 생성합니다.

비밀번호는 기억하거나 어딘가에 적어놓도록 합니다.

 

 

비밀번호가 너무 간단하다고 하는데 위에 있는 Proceed ...라고 적힌 상단 글씨를 클릭하면 그냥 생성됩니다.

 

 

 

User가 생성되었습니다.

 

 

 

그룹도 동일한 방법으로 Groups에 마우스를 놓고 우클릭을 합니다.

[Create Group...] 메뉴를 클릭 합니다.

 

 

 

그룹 이름을 적당히 입력하고 그룹에 멤버를 추가하기 위해 하단의 [Add...] 버튼을 클릭해 봅니다.

 

 

 

조금 전 생성한 User가 보입니다. 

선택하고 [OK] 버튼을 클릭합니다.

  

 

 

 

together 그룹에 추가되었습니다.

추가할 사용자가 더 없으니 그냥 [OK] 버튼을 클릭합니다.

 

 

 

그룹이 멤버를 포함하면서 생성이 되었습니다.

 

 

이제 생성한 Repository를 사용할 수 있는 사용자 또는 그룹을 연결하는 작업을 시작해 봅니다.

 

 

생성한 TestRepository에 마우스를 놓고 우클릭하여 [Properties...]를 클릭합니다.

 

 

 

Everyone이라는 그룹이 기본으로 포함되어 있습니다. 

조금전에 생성한 그룹을 포함하기 위해 [Add...] 버튼을 클릭합니다.

 

 

 

조금전에 생성한 together 그룹이 보입니다. 선택하고 [OK] 버튼을 클릭합니다.

물론 아래에 있는 user를 한명, 한명 추가하려면 하단에 있는 Users에서 선택을 해도 되지만 그럼 사용자가 많은 경우 관리가 어렵게 됩니다.

 

 

 

기본으로 추가된 Everyone은 아무나 접속 가능한 설정이므로 접속을 차단해야 합니다.

Everyone을 선택하고 하단에 있는 No Access 라디오 버튼을 선택한 후 [OK] 버튼을 클릭하면 접속권한이 취소됩니다.

 

 

 

 

이런식으로 사용자와 그룹을 관리하면 됩니다.

 

다음번에는 Repository Project Share하고 commit 해보도록 하겠습니다.

 

- copy coding -

 

오랬 동안 Eclipse를 사용하여 개발을 해왔는데 최근에 IntellJ를 사용할 기회가 되어 설치부터 차근차근 정리를 해보려고 합니다.

 

JetBrains 사이트에 접속하여 설치 프로그램을 다운로드 합니다.

 

https://www.jetbrains.com/ko-kr/idea/download/?section=windows

 

 

다운로드 페이지에 접속해서 처음에 보이는 IntelliJ IDEA Ultimate는 유료 버전으로 30일 평가판이고 아래쪽으로 내려가면 IntelliJ IDEA Community Edition이 보입니다.

 

 

 

[다운로드]를 클릭하여 IntelliJ IDEA Community Edition 설치파일을 다운로드 받습니다.

 

다운받은 설치파일(ideaIC-2023.3.exe)을 실행하여 설치를 위한 설정 작업을 시작합니다.

설정 및 설치 작업은 간단 합니다.

 

 

[Next] 버튼을 클릭합니다.

 

 

설치 위치를 선택하거나 기본 값을 그대로 사용하여 [Next] 버튼을 클릭합니다.

 

 

 

설치 옵션 선택입니다.

 

Create Desktop Shortcut : 바탕화면에 바로가기 생성 여부입니다.

Update PATH variable(restart needed) : 윈도우 환경변수에 자동으로 추가해 줍니다.

Update Context menu : 프로젝트 폴더를 선택해서 바로 작업 가능하도록 설정합니다.

Create Association : 사용 언어와 환경을 선택하는 것으로 복수선택이 가능합니다.

 

필요한 항목을 체크하고 [Next] 버튼을 클릭합니다.

 

 

여기까지 환경 설정 작업이었습니다.

[Install] 버튼을 클릭하여 설치를 진행합니다.

 

 

 

설치가 진행되고 잠시 후 완료됩니다.

 

 

 

컴퓨터를 새로 시작해서 바로 환경설정을 적용하거나 나중에 재부팅을 선택합니다.

일단 나중에 재부팅 하기로 하고 [Finish] 버튼을 클릭하여 설치된 Intellij 프로그램을 실행해 봅니다.

 

 

 

사용전에 라이센스 동의를 해야 합니다.  체크박스에 체크하면 [Continue] 버튼이 활성화됩니다.

[Continue] 버튼을 클릭합니다.

 

 

 

인텔리제이를 사용하면서 사용환경 등 여러가지 정보를 보내달라고 하는데 [Don’t Send]를 클릭해서 거절합니다.

 

 

 

드디어 Intellij를 사용할 수 있는 화면에 도달했습니다.

 

좌측에 환경설정을 할 수 있는 메뉴들이 있는데 필요에 따라 하나씩 설정을 하면 됩니다.

간단하게 한번씩 클릭을 해봅니다.

 

 

 

[Customize]를 클릭하면 우측에서 테마는 검은색 그대로 놔두고 디스플레이가 1920x1080인 경우는 글자가 너무 크게 보여서 폰트는 13.0에서 조금 작게 선택했습니다.

 

 

 

[Plugins]에서 Marketplace에는 설치할 수 있는 플러그인이 있고 Installed에는 기존에 설치되어 있는 Plugin들을 볼 수 있습니다.

 

[Learn]은 사용법에 대한 학습을 할 수 있는 새로운 팝업 창이 나타납니다.

 

다시 좌측메뉴에서 [Projects]를 선택하고 우측에서  [New Project]를 선택해서 신규로 프로젝트를 하나 생성해보도록 하겠습니다.

 

 

 

대부분 알아서 입력 또는 선택하면 되는데 JDK는 기존에 설치한 프로그램이 없으면 바로 다운로드 가능 합니다.

 

 

 

여러 버전 중 하나를 선택하고 설치 위치도 설정해서 [Download] 버튼을 클릭합니다.

 

 

 

바로 설치가 진행됩니다.

 

 

 

[Advanced Settings]를 확장하면 기본값이 설정되어 있는데 필요한 내용으로 수정합니다.

 

[Create] 버튼을 클릭하면 프로젝트가 생성됩니다.

 

 

 

프로젝트가 생성된 모습입니다.

Pom.xml 파일을 보니 너무 심플하네요.  한참을 채워 넣어야 할 것 같습니다.

상단에 있는 실행 버튼을 클릭해서 프로젝트를 실행해 봅니다.

 

 

 

Java도 잘 실행이 되는군요.

 

 

 

선택한 폴더에 프로젝트 파일도 생성되었습니다.

사용해 보면서 하나씩 추가적인 기능 설명도 정리를 해보겠습니다.

 

- copy coding -

 

 

이전 글에서 Google에서 학습을 위해 제공하는 OpenGL ES 3.0 관련 소스를 이용하여 삼각형과 사각형을 그려보았습니다.

 

Android OpenGL ES 3.0 삼각형 그리기(java 버전)

 


Android OpenGL ES 3.0 사각형 그리기(java 버전)

 

 

오늘은 직육면체를 그려보려고 하는데 소스는 구글 개발자 사이트에서 찾을 수 없어 인터넷을 찾아보다가 https://github.com/JimSeker/opengl 에서 적당한 소스를 찾아 소개를 드립니다.

프로그램 구성이 구글에서 설명하는 방식과 거의 동일한 구조로 되어있어서 이해하기가 쉬울것 같아 선택했습니다.

 

최종 구현 모습은 여러 곳에서 한번쯤 본듯한 형태입니다.

 

 

 

 

사각형을 그릴때 설명한것 처럼 삼각형을 2개 연결해서 사각형을 그렸습니다.  이번에도 삼각형을 그려서 사각형을 만들고 이 사각형들을 모아서 6면체를 만들게 됩니다.

사용되는 java 프로그램 소스는 처음에 삼각형 그리기 편에서 설명했던 내용이 변함 없이 적용 됩니다.  기본이되는 2개의 클래스가 있는데 이 클래스에 대한 자세한 설명은 삼각형 그리기를 참고하면 되고 여기서는 간략하게 어떤 내용이 있었는지 제목 정도만 알아보겠습니다.

 

GLSurfaceView 클래스 : 객체를 그리고 조작할 수 있는 View 이며 터치 리스너를 구현할 수 있습니다.

 

GLSurfaceView.Renderer 클래스 : 3개의 기본 메서드를 구현해야합니다.

  - onSurfaceCreated() : 최초 한번 호출되며 초기화가 필요한 내용 기술.

  - onDrawFrame() : GLSurfaceView를 다시 그릴때마다 호출되며 Object를 움직이거나 변형 가능.

  - onSurfaceChanged() : GLSurfaceView의 크기, 방향 변경시 호출된다.

 

 

다른 도형과 다르게 Cube 그리기에서 추가되는 부분이 있는데 터치 관련 기능입니다.  GLSurfaceVIew 클래스 설명에 터치 이벤트를 구현한다고 되어있는것 처럼 GLSurfaceView 클래스에 onTouchEvent()가 추가 됩니다.  구현할 때

 

private static final float TOUCH_SCALE_FACTOR = 0.0015f;

 

이 값을 변경하면 화면을 터치하고 Object를 이동 시킬때 한번에 이동하는 거리를 조절할 수 있습니다.

 

Renderer 프로그램에는 onDrawFrame()에서 물체의 변형이나 움직임을 구현할 수 있다고 하였는데 여기서는 육면체를 회전시키는 작업이 추가됩니다.  코드에서

 

mAngle+=4;

 

이 각도값을 변경하면 회전하는 속도를 조절할 수 있습니다.

 

myColor.java 라는 파일이 하나 추가되었는데 삼각형, 사각형은 2차원 이미지라 색상 값을 그냥 코드에 추가를 하였는데 정육면체는 6개의 색을 표현하기 위해 별도의 파일로 만들어 놓았습니다. 귀찮으면 그냥 파일을 만들지 말고 구현하거나 단색으로 테스트해도 됩니다.

설명을 이정도로 하고 실행 했을 때의 모습을 보겠습니다.

 

 

 

 

아래는 전체 코드입니다.  수정하지 않은 파일을 첨부하지 않았습니다.

위에서 소개한 페이지로 가서 소스를 다운받아 테스트 하셔도 됩니다.

 

AndroidManifest.xml

 

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools">



    <uses-feature android:glEsVersion="0x00030000" android:required="true" />



    <application

        android:allowBackup="true"

        android:dataExtractionRules="@xml/data_extraction_rules"

        android:fullBackupContent="@xml/backup_rules"

        android:icon="@mipmap/ic_launcher"

        android:label="@string/app_name"

        android:roundIcon="@mipmap/ic_launcher_round"

        android:supportsRtl="true"

        android:theme="@style/Theme.OpenGLTest"

        tools:targetApi="31">

        <activity

            android:name=".MainActivity"

            android:exported="true">

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />



                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>



</manifest>

 

 

 

 

myGlSurfaceView.java

 

package com.example.opengltest;



import android.content.Context;

import android.opengl.GLSurfaceView;

import android.view.MotionEvent;



public class myGlSurfaceView extends GLSurfaceView {



    myRenderer myRender;



    public myGlSurfaceView(Context context) {

        super(context);



        // Create an OpenGL ES 3.0 context.

        setEGLContextClientVersion(3);



        super.setEGLConfigChooser(8, 8, 8, 8, 16, 0);



        // Set the Renderer for drawing on the GLSurfaceView

        myRender = new myRenderer(context);

        setRenderer(myRender);



        // Render the view only when there is a change in the drawing data

        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);

    }



    //private final float TOUCH_SCALE_FACTOR = 180.0f / 320;

    private static final float TOUCH_SCALE_FACTOR = 0.0015f;

    private float mPreviousX;

    private float mPreviousY;



    @Override

    public boolean onTouchEvent(MotionEvent e) {

//        return super.onTouchEvent(event);



        float x = e.getX();

        float y = e.getY();



        switch (e.getAction()) {

            case MotionEvent.ACTION_MOVE:



                float dx = x - mPreviousX;

                //subtract, so the cube moves the same direction as your finger.

                //with plus it moves the opposite direction.

                myRender.setX(myRender.getX() - (dx * TOUCH_SCALE_FACTOR));



                float dy = y - mPreviousY;

                myRender.setY(myRender.getY() - (dy * TOUCH_SCALE_FACTOR));

        }



        mPreviousX = x;

        mPreviousY = y;

        return true;

    }

}

 

 

 

 

myRenderer.java

 

package com.example.opengltest;



import android.content.Context;

import android.opengl.GLES30;

import android.opengl.GLSurfaceView;

import android.opengl.Matrix;

import android.util.Log;



import javax.microedition.khronos.egl.EGLConfig;

import javax.microedition.khronos.opengles.GL10;



public class myRenderer implements GLSurfaceView.Renderer {



    private int mWidth;

    private int mHeight;

    private static String TAG = "myRenderer";

    public Cube mCube;

    private float mAngle =0;

    private float mTransY=0;

    private float mTransX=0;

    private static final float Z_NEAR = 1f;

    private static final float Z_FAR = 40f;



    // mMVPMatrix is an abbreviation for "Model View Projection Matrix"

    private final float[] mMVPMatrix = new float[16];

    private final float[] mProjectionMatrix = new float[16];

    private final float[] mViewMatrix = new float[16];

    private final float[] mRotationMatrix = new float[16];





    public myRenderer(Context context) {

        //cube can not be instianated here, because of "no egl context"  no clue.

        //do it in onSurfaceCreate and it is fine.  odd, but workable solution.

    }

    public static int LoadShader(int type, String shaderSrc) {

        int shader;

        int[] compiled = new int[1];



        // Create the shader object

        shader = GLES30.glCreateShader(type);



        if (shader == 0) {

            return 0;

        }



        // Load the shader source

        GLES30.glShaderSource(shader, shaderSrc);



        // Compile the shader

        GLES30.glCompileShader(shader);



        // Check the compile status

        GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);



        if (compiled[0] == 0) {

            Log.e(TAG, "Erorr!!!!");

            Log.e(TAG, GLES30.glGetShaderInfoLog(shader));

            GLES30.glDeleteShader(shader);

            return 0;

        }



        return shader;

    }



    public static void checkGlError(String glOperation) {

        int error;

        while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR) {

            Log.e(TAG, glOperation + ": glError " + error);

            throw new RuntimeException(glOperation + ": glError " + error);

        }

    }



    @Override

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {



        //set the clear buffer color to light gray.

        //GLES30.glClearColor(0.9f, .9f, 0.9f, 0.9f);

        //set the clear buffer color to a dark grey.

        GLES30.glClearColor(0.1f, .1f, 0.1f, 0.9f);

        //initialize the cube code for drawing.

        mCube = new Cube();

        //if we had other objects setup them up here as well.

    }



    @Override

    public void onSurfaceChanged(GL10 gl, int width, int height) {



        mWidth = width;

        mHeight = height;

        // Set the viewport

        GLES30.glViewport(0, 0, mWidth, mHeight);

        float aspect = (float) width / height;



        // this projection matrix is applied to object coordinates

        //no idea why 53.13f, it was used in another example and it worked.

        Matrix.perspectiveM(mProjectionMatrix, 0, 53.13f, aspect, Z_NEAR, Z_FAR);

    }



    @Override

    public void onDrawFrame(GL10 gl) {



        // Clear the color buffer  set above by glClearColor.

        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);



        //need this otherwise, it will over right stuff and the cube will look wrong!

        GLES30.glEnable(GLES30.GL_DEPTH_TEST);



        // Set the camera position (View matrix)  note Matrix is an include, not a declared method.

        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);



        // Create a rotation and translation for the cube

        Matrix.setIdentityM(mRotationMatrix, 0);



        //move the cube up/down and left/right

        Matrix.translateM(mRotationMatrix, 0, mTransX, mTransY, 0);



        //mangle is how fast, x,y,z which directions it rotates.

        Matrix.rotateM(mRotationMatrix, 0, mAngle, 1.0f, 1.0f, 1.0f);



        // combine the model with the view matrix

        Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mRotationMatrix, 0);



        // combine the model-view with the projection matrix

        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);



        mCube.draw(mMVPMatrix);



        //change the angle, so the cube will spin.

        mAngle+=4;

    }



    //used the touch listener to move the cube up/down (y) and left/right (x)

    public float getY() {

        return mTransY;

    }



    public void setY(float mY) {

        mTransY = mY;

    }



    public float getX() {

        return mTransX;

    }



    public void setX(float mX) {

        mTransX = mX;

    }

}

 

 

Cube.java

 

package com.example.opengltest;



import android.opengl.GLES30;

import android.util.Log;



import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;



public class Cube {



    private int mProgramObject;

    private int mMVPMatrixHandle;

    private int mColorHandle;

    private FloatBuffer mVertices;



    //initial size of the cube.  set here, so it is easier to change later.

    float size = 0.4f;



    //this is the initial data, which will need to translated into the mVertices variable in the consturctor.

    float[] mVerticesData = new float[]{

            ////////////////////////////////////////////////////////////////////

            // FRONT

            ////////////////////////////////////////////////////////////////////

            // Triangle 1

            -size, size, size, // top-left

            -size, -size, size, // bottom-left

            size, -size, size, // bottom-right

            // Triangle 2

            size, -size, size, // bottom-right

            size, size, size, // top-right

            -size, size, size, // top-left

            ////////////////////////////////////////////////////////////////////

            // BACK

            ////////////////////////////////////////////////////////////////////

            // Triangle 1

            -size, size, -size, // top-left

            -size, -size, -size, // bottom-left

            size, -size, -size, // bottom-right

            // Triangle 2

            size, -size, -size, // bottom-right

            size, size, -size, // top-right

            -size, size, -size, // top-left



            ////////////////////////////////////////////////////////////////////

            // LEFT

            ////////////////////////////////////////////////////////////////////

            // Triangle 1

            -size, size, -size, // top-left

            -size, -size, -size, // bottom-left

            -size, -size, size, // bottom-right

            // Triangle 2

            -size, -size, size, // bottom-right

            -size, size, size, // top-right

            -size, size, -size, // top-left

            ////////////////////////////////////////////////////////////////////

            // RIGHT

            ////////////////////////////////////////////////////////////////////

            // Triangle 1

            size, size, -size, // top-left

            size, -size, -size, // bottom-left

            size, -size, size, // bottom-right

            // Triangle 2

            size, -size, size, // bottom-right

            size, size, size, // top-right

            size, size, -size, // top-left



            ////////////////////////////////////////////////////////////////////

            // TOP

            ////////////////////////////////////////////////////////////////////

            // Triangle 1

            -size, size, -size, // top-left

            -size, size, size, // bottom-left

            size, size, size, // bottom-right

            // Triangle 2

            size, size, size, // bottom-right

            size, size, -size, // top-right

            -size, size, -size, // top-left

            ////////////////////////////////////////////////////////////////////

            // BOTTOM

            ////////////////////////////////////////////////////////////////////

            // Triangle 1

            -size, -size, -size, // top-left

            -size, -size, size, // bottom-left

            size, -size, size, // bottom-right

            // Triangle 2

            size, -size, size, // bottom-right

            size, -size, -size, // top-right

            -size, -size, -size // top-left

    };



    float colorcyan[] = myColor.cyan();

    float colorblue[] = myColor.blue();

    float colorred[] = myColor.red();

    float colorgray[] = myColor.gray();

    float colorgreen[] = myColor.green();

    float coloryellow[] = myColor.yellow();



    //vertex shader code

    String vShaderStr =

            "#version 300 es           \n"

                    + "uniform mat4 uMVPMatrix;     \n"

                    + "in vec4 vPosition;           \n"

                    + "void main()                  \n"

                    + "{                            \n"

                    + "   gl_Position = uMVPMatrix * vPosition;  \n"

                    + "}                            \n";

    //fragment shader code.

    String fShaderStr =

            "#version 300 es                          \n"

                    + "precision mediump float;                   \n"

                    + "uniform vec4 vColor;                      \n"

                    + "out vec4 fragColor;                   \n"

                    + "void main()                                  \n"

                    + "{                                            \n"

                    + "  fragColor = vColor;                       \n"

                    + "}                                            \n";



    String TAG = "Cube";





    //finally some methods

    //constructor

    public Cube() {

        //first setup the mVertices correctly.

        mVertices = ByteBuffer

                .allocateDirect(mVerticesData.length * 4)

                .order(ByteOrder.nativeOrder())

                .asFloatBuffer()

                .put(mVerticesData);

        mVertices.position(0);



        //setup the shaders

        int vertexShader;

        int fragmentShader;

        int programObject;

        int[] linked = new int[1];



        // Load the vertex/fragment shaders

        vertexShader = myRenderer.LoadShader(GLES30.GL_VERTEX_SHADER, vShaderStr);

        fragmentShader = myRenderer.LoadShader(GLES30.GL_FRAGMENT_SHADER, fShaderStr);



        // Create the program object

        programObject = GLES30.glCreateProgram();



        if (programObject == 0) {

            Log.e(TAG, "So some kind of error, but what?");

            return;

        }



        GLES30.glAttachShader(programObject, vertexShader);

        GLES30.glAttachShader(programObject, fragmentShader);



        // Bind vPosition to attribute 0

        GLES30.glBindAttribLocation(programObject, 0, "vPosition");



        // Link the program

        GLES30.glLinkProgram(programObject);



        // Check the link status

        GLES30.glGetProgramiv(programObject, GLES30.GL_LINK_STATUS, linked, 0);



        if (linked[0] == 0) {

            Log.e(TAG, "Error linking program:");

            Log.e(TAG, GLES30.glGetProgramInfoLog(programObject));

            GLES30.glDeleteProgram(programObject);

            return;

        }



        // Store the program object

        mProgramObject = programObject;



        //now everything is setup and ready to draw.

    }



    public void draw(float[] mvpMatrix) {



        // Use the program object

        GLES30.glUseProgram(mProgramObject);



        // get handle to shape's transformation matrix

        mMVPMatrixHandle = GLES30.glGetUniformLocation(mProgramObject, "uMVPMatrix");

        myRenderer.checkGlError("glGetUniformLocation");



        // get handle to fragment shader's vColor member

        mColorHandle = GLES30.glGetUniformLocation(mProgramObject, "vColor");





        // Apply the projection and view transformation

        GLES30.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

        myRenderer.checkGlError("glUniformMatrix4fv");



        int VERTEX_POS_INDX = 0;

        mVertices.position(VERTEX_POS_INDX);  //just in case.  We did it already though.



        //add all the points to the space, so they can be correct by the transformations.

        //would need to do this even if there were no transformations actually.

        GLES30.glVertexAttribPointer(VERTEX_POS_INDX, 3, GLES30.GL_FLOAT,

                false, 0, mVertices);

        GLES30.glEnableVertexAttribArray(VERTEX_POS_INDX);



        //Now we are ready to draw the cube finally.

        int startPos =0;

        int verticesPerface = 6;



        //draw front face

        GLES30.glUniform4fv(mColorHandle, 1, colorblue, 0);

        GLES30.glDrawArrays(GLES30.GL_TRIANGLES,startPos,verticesPerface);

        startPos += verticesPerface;



        //draw back face

        GLES30.glUniform4fv(mColorHandle, 1, colorcyan, 0);

        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, startPos, verticesPerface);

        startPos += verticesPerface;



        //draw left face

        GLES30.glUniform4fv(mColorHandle, 1, colorred, 0);

        GLES30.glDrawArrays(GLES30.GL_TRIANGLES,startPos,verticesPerface);

        startPos += verticesPerface;



        //draw right face

        GLES30.glUniform4fv(mColorHandle, 1, colorgray, 0);

        GLES30.glDrawArrays(GLES30.GL_TRIANGLES,startPos,verticesPerface);

        startPos += verticesPerface;



        //draw top face

        GLES30.glUniform4fv(mColorHandle, 1, colorgreen, 0);

        GLES30.glDrawArrays(GLES30.GL_TRIANGLES,startPos,verticesPerface);

        startPos += verticesPerface;



        //draw bottom face

        GLES30.glUniform4fv(mColorHandle, 1, coloryellow, 0);

        GLES30.glDrawArrays(GLES30.GL_TRIANGLES,startPos,verticesPerface);

        //last face, so no need to increment.



    }

}

 

 

 

 

myColor.java

 

package com.example.opengltest;



import android.graphics.Color;



public class myColor {



    static float[] red() {

        return new float[]{

                Color.red(Color.RED) / 255f,

                Color.green(Color.RED) / 255f,

                Color.blue(Color.RED) / 255f,

                1.0f

        };

    }



    static float[] green() {

        return new float[]{

                Color.red(Color.GREEN) / 255f,

                Color.green(Color.GREEN) / 255f,

                Color.blue(Color.GREEN) / 255f,

                1.0f

        };

    }



    static float[] blue() {

        return new float[]{

                Color.red(Color.BLUE) / 255f,

                Color.green(Color.BLUE) / 255f,

                Color.blue(Color.BLUE) / 255f,

                1.0f

        };

    }



    static float[] yellow() {

        return new float[]{

                Color.red(Color.YELLOW) / 255f,

                Color.green(Color.YELLOW) / 255f,

                Color.blue(Color.YELLOW) / 255f,

                1.0f

        };

    }



    static float[] cyan() {

        return new float[]{

                Color.red(Color.CYAN) / 255f,

                Color.green(Color.CYAN) / 255f,

                Color.blue(Color.CYAN) / 255f,

                1.0f

        };

    }



    static float[] gray() {

        return new float[]{

                Color.red(Color.GRAY) / 255f,

                Color.green(Color.GRAY) / 255f,

                Color.blue(Color.GRAY) / 255f,

                1.0f

        };

    }

}

 

 

MainActivity.java

 

package com.example.opengltest;



import androidx.appcompat.app.AppCompatActivity;



import android.app.ActivityManager;

import android.content.Context;

import android.content.pm.ConfigurationInfo;

import android.os.Bundle;

import android.util.Log;

import android.view.View;



public class MainActivity extends AppCompatActivity {



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

//        setContentView(R.layout.activity_main);



        if (detectOpenGLES30()) {

            //so we know it a opengl 3.0 and use our extended GLsurfaceview.

            setContentView(new myGlSurfaceView(this));

        } else {

            // This is where you could create an OpenGL ES 2.0 and/or 1.x compatible

            // renderer if you wanted to support both ES 1 and ES 2.

            Log.e("openglcube", "OpenGL ES 3.0 not supported on device.  Exiting...");

            finish();



        }

    }



    private boolean detectOpenGLES30() {

        ActivityManager am =

                (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

        ConfigurationInfo info = am.getDeviceConfigurationInfo();

        return (info.reqGlEsVersion >= 0x30000);

    }



    @Override

    public void onWindowFocusChanged(boolean hasFocus) {

        super.onWindowFocusChanged(hasFocus);



        if (hasFocus) {

            getWindow().getDecorView().setSystemUiVisibility(

                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE

                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION

                            | View.SYSTEM_UI_FLAG_FULLSCREEN

                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

        }

    }

}

 

 - copy coding -

 

Android에서 OpenGL ES 3.0 버전을 이용하여 사각형을 그리는 예제입니다.

Google developer 사이트에서 OPenGL을 이용하여 Android에서 삼각형과 사각형 도형을 그리기 위한 설명을 하는 페이지를 간단히 요약하여 소개하고 제공하는 소스를 이용하여 도형을 제작하는 방법까지 바로 이전 글에서 삼각형 관련된 예제로 알아보았습니다.

 


Android OpenGL ES 3.0
삼각형 그리기(java 버전)



 

그래서 자세한 설명은 이전 글을 참고하시기 바라며 이번에는 동일한 소스를 사용하여 사각형을 그리는 부분만 추가하여 테스트를 진행합니다.

삼각형을 그릴때는 Triangle.java 파일 만들었는데 사각형을 그리기 위해서 Square.java 파일을 생성하는데 일부 변경이 되고 Renderer 파일도 조금 수정해주면 됩니다.

 

다른 소스는 전에 생성한 내용을 그대로 사용하고 여기서는 SquareRenderer 파일의 변경된 부분만 설명합니다.

 

실행했을 때 결과화면은 아래와 같이 멋없는 사각형입니다.

 

 

 

 

Square.java 파일을 생성하고 사각형 용으로 도형의 좌표를 표기합니다.

 

 

static float squareCoords[] = {
            -0.5f,  0.5f, 0.0f,   // top left
            -0.5f, -0.5f, 0.0f,   // bottom left
             0.5f, -0.5f, 0.0f,   // bottom right
             0.5f,  0.5f, 0.0f }; // top right

 

그림에 있는 좌표를 표시하고 그리는 순서를 정하여 꼭지점의 순서를 정하게 됩니다.

특이한점은 사각형을 사각형으로 그리지 않고 3각형 2개를 그리는 방법으로 진행합니다.

private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

이렇게 시계 반대방향으로 순서를 나열하여 좌측과 우측의 삼각형을 그리도록 정의합니다.

 

// initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(
                // (# of coordinate values * 2 bytes per short)
                drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);

 

이런 작업 순서는 ByteBuffer에 저장해 둡니다.

나머지 소스들은 삼각형일때 triangleCoords로 되어있던 부분을 squareCoords로 변경합니다.

 

draw() 함수에서 삼각형은 꼭지점을 이용하여 그리고 사각형은 Buffer에 있는 순서를 이용하여 그리도록 수정됩니다.

 

// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
 
// Draw the square
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
                GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
 

 

 

MyGLRenderer.java 파일에서는 MVP Matrix가 사용됩니다.

MVP(Model, View, Projection)은 카메라에서 모델을 바라보는 형상을 표시하는 것인데

삼각형에서는 사용하지 않았는데 사각형에서는 왜 필요한지 지식이 짧아 좀더 학습이 필요하네요.

 

@Override
    public void onDrawFrame(GL10 gl) {
        float[] scratch = new float[16];
        // Draw background color
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
        // Set the camera position (View matrix)
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        // Calculate the projection and view transformation
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
        // Draw square
        mSquare.draw(mMVPMatrix);
    }

 

그리는 부분에서 삼각형은 Array, 사각형은 Element의 차이인데 나중에 알게되면 내용 추가를 해야 할 것 같습니다.

수정되는 부분이 많지 않아 설명이 짧게 끝났습니다.

 

아래는 전체 소스입니다.

 

AndroidManifest.xml

 

<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
   
xmlns:tools="http://schemas.android.com/tools">

    <uses-feature
android:glEsVersion="0x00030000" android:required="true" />

    <application
       
android:allowBackup="true"
       
android:dataExtractionRules="@xml/data_extraction_rules"
       
android:fullBackupContent="@xml/backup_rules"
       
android:icon="@mipmap/ic_launcher"
       
android:label="@string/app_name"
       
android:roundIcon="@mipmap/ic_launcher_round"
       
android:supportsRtl="true"
       
android:theme="@style/Theme.OpenGLTriangle"
       
tools:targetApi="31">
        <activity
           
android:name=".MainActivity"
           
android:exported="true">
            <intent-filter>
                <action
android:name="android.intent.action.MAIN" />

                <category
android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

 

MainActivity.java

 

package com.copycoding.opengltriangle;

import
androidx.appcompat.app.AppCompatActivity;

import
android.opengl.GLSurfaceView;
import
android.os.Bundle;

public class
MainActivity extends AppCompatActivity {
   
private GLSurfaceView gLView;

   
@Override
   
protected void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);
        // Create a GLSurfaceView instance and set it
        // as the ContentView for this Activity.
       
gLView = new MyGLSurfaceView(this);
       
setContentView(gLView);
   
}
}

 

 

 

MyGLRenderer.java

  

package com.copycoding.openglsquare;



import android.opengl.GLES30;

import android.opengl.GLSurfaceView;

import android.opengl.Matrix;

import android.util.Log;



import javax.microedition.khronos.egl.EGLConfig;

import javax.microedition.khronos.opengles.GL10;



public class MyGLRenderer implements GLSurfaceView.Renderer {



//    private Triangle mTriangle;

    private Square mSquare;



    private static final String TAG = "MyGLRenderer";

    private final float[] mMVPMatrix = new float[16];

    private final float[] mProjectionMatrix = new float[16];

    private final float[] mViewMatrix = new float[16];

    private final float[] mRotationMatrix = new float[16];

    private float mAngle;



    @Override

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        // Set the background frame color

        GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        // initialize a triangle

        mSquare = new Square();

    }



    @Override

    public void onSurfaceChanged(GL10 gl, int width, int height) {

        GLES30.glViewport(0, 0, width, height);

        float ratio = (float) width / height;

        // this projection matrix is applied to object coordinates

        // in the onDrawFrame() method

        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);

    }



    @Override

    public void onDrawFrame(GL10 gl) {

        float[] scratch = new float[16];

        // Draw background color

        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);

        // Set the camera position (View matrix)

        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

        // Calculate the projection and view transformation

        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

        // Draw square

        mSquare.draw(mMVPMatrix);

    }

    public static int loadShader(int type, String shaderCode){



        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)

        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)

        int shader = GLES30.glCreateShader(type);



        // add the source code to the shader and compile it

        GLES30.glShaderSource(shader, shaderCode);

        GLES30.glCompileShader(shader);



        return shader;

    }



    public static void checkGlError(String glOperation) {

        int error;

        while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR) {

            Log.e(TAG, glOperation + ": glError " + error);

            throw new RuntimeException(glOperation + ": glError " + error);

        }

    }

}

 

 

 

MyGLSurfaceView.java

 

package com.copycoding.opengltriangle;

import
android.content.Context;
import
android.opengl.GLSurfaceView;

public class
MyGLSurfaceView extends GLSurfaceView {

   
private final MyGLRenderer renderer;
    public
MyGLSurfaceView(Context context) {
       
super(context);

       
// Create an OpenGL ES 2.0 context
       
setEGLContextClientVersion(2);

       
renderer = new MyGLRenderer();

       
// Set the Renderer for drawing on the GLSurfaceView
       
setRenderer(renderer);
   
}
}

 

 

Square.java

 

package com.copycoding.openglsquare;



import android.opengl.GLES30;



import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;

import java.nio.ShortBuffer;



public class Square {



    private final String vertexShaderCode =

            // This matrix member variable provides a hook to manipulate

            // the coordinates of the objects that use this vertex shader

            "uniform mat4 uMVPMatrix;" +

                    "attribute vec4 vPosition;" +

                    "void main() {" +

                    // The matrix must be included as a modifier of gl_Position.

                    // Note that the uMVPMatrix factor *must be first* in order

                    // for the matrix multiplication product to be correct.

                    "  gl_Position = uMVPMatrix * vPosition;" +

                    "}";

    private final String fragmentShaderCode =

            "precision mediump float;" +

                    "uniform vec4 vColor;" +

                    "void main() {" +

                    "  gl_FragColor = vColor;" +

                    "}";



    private FloatBuffer vertexBuffer;

    private ShortBuffer drawListBuffer;



    // number of coordinates per vertex in this array

    static final int COORDS_PER_VERTEX = 3;

    static float squareCoords[] = {

            -0.5f,  0.5f, 0.0f,   // top left

            -0.5f, -0.5f, 0.0f,   // bottom left

             0.5f, -0.5f, 0.0f,   // bottom right

             0.5f,  0.5f, 0.0f    // top right

    };

    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices



    // Set color with red, green, blue and alpha (opacity) values

    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };



    private final int mProgram;



    private int positionHandle;

    private int colorHandle;

    private int mVPMatrixHandle;



//    private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;

    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex





    public Square() {

        // initialize vertex byte buffer for shape coordinates

        ByteBuffer bb = ByteBuffer.allocateDirect(

                // (number of coordinate values * 4 bytes per float)

                squareCoords.length * 4);

        // use the device hardware's native byte order

        bb.order(ByteOrder.nativeOrder());



        // create a floating point buffer from the ByteBuffer

        vertexBuffer = bb.asFloatBuffer();

        // add the coordinates to the FloatBuffer

        vertexBuffer.put(squareCoords);

        // set the buffer to read the first coordinate

        vertexBuffer.position(0);



        // initialize byte buffer for the draw list

        ByteBuffer dlb = ByteBuffer.allocateDirect(

                // (# of coordinate values * 2 bytes per short)

                drawOrder.length * 2);

        dlb.order(ByteOrder.nativeOrder());

        drawListBuffer = dlb.asShortBuffer();

        drawListBuffer.put(drawOrder);

        drawListBuffer.position(0);



        int vertexShader = MyGLRenderer.loadShader(GLES30.GL_VERTEX_SHADER,

                vertexShaderCode);

        int fragmentShader = MyGLRenderer.loadShader(GLES30.GL_FRAGMENT_SHADER,

                fragmentShaderCode);



        // create empty OpenGL ES Program

        mProgram = GLES30.glCreateProgram();



        // add the vertex shader to program

        GLES30.glAttachShader(mProgram, vertexShader);



        // add the fragment shader to program

        GLES30.glAttachShader(mProgram, fragmentShader);



        // creates OpenGL ES program executables

        GLES30.glLinkProgram(mProgram);



    }



    public void draw(float[] mvpMatrix) {

        // Add program to OpenGL ES environment

        GLES30.glUseProgram(mProgram);



        // get handle to vertex shader's vPosition member

        positionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");



        // Enable a handle to the triangle vertices

        GLES30.glEnableVertexAttribArray(positionHandle);



        // Prepare the triangle coordinate data

        GLES30.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,

                GLES30.GL_FLOAT, false,

                vertexStride, vertexBuffer);



        // get handle to fragment shader's vColor member

        colorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");



        // Set color for drawing the triangle

        GLES30.glUniform4fv(colorHandle, 1, color, 0);







        // get handle to shape's transformation matrix

        mVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");

        MyGLRenderer.checkGlError("glGetUniformLocation");

        // Apply the projection and view transformation

        GLES30.glUniformMatrix4fv(mVPMatrixHandle, 1, false, mvpMatrix, 0);

        MyGLRenderer.checkGlError("glUniformMatrix4fv");

        // Draw the square

        GLES30.glDrawElements(

                GLES30.GL_TRIANGLES, drawOrder.length,

                GLES30.GL_UNSIGNED_SHORT, drawListBuffer);







        // Draw the triangle

//        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount);



        // Disable vertex array

        GLES30.glDisableVertexAttribArray(positionHandle);

    }

}

 

소스 바탕색이 이상하게 변하네요...

 

 - copy coding -


1234567···116

+ Recent posts