3D그래픽2011. 10. 22. 10:07
onDrawFrame은 Android에서 OpenGL ES를 이용할 때 사용해야하는 GLSurfaceView.Renderer interface의 한 method이다.
매 프레임이 그려질 때 호출 되는 method 이므로, 이 method를 어떻게 구성하느냐에 따라 성능에 영향을 준다.

OpenGL ES 2.0를 이용하면서 한동안 고민했던 것들 중 하나가, uniform이나 attribute의 reference index를 관리하는 방법이었다. 보통 일반적인 예제에서는 onSurfaceCreated에서 glGetUniformLocation API를 이용해서 미리 저장을 해 두고 활용하게된다.

그런데, shader program이 runtime에 변경되는 경우 어떻게 해야 할지가 고민이었다. 이러한 경우에 여러개의 program들을 미리 생성해서 reference index를 미리 저장해 둔다는게 적절한지 의문이 들었기 때문이다.
그렇다고, 매 프레임을 그리면서 매번 glGetUniformLocation을 이용해 ref. index를 획득한다면 성능에 문제가 있지 않을까 라는 고민도 있었다.

그런데, 인터넷에서 몇몇 예제들을 확인 해 보니, runtime에 glGetUniformLocation을 이용하는 것이었다. 그래서 이렇게 해도괜찮은가보다 라고 생각하고 코드를 수정했고 정상적으로 잘 동작했다.

한동안 잊고 있다가, 어제 LogCat에서 log를 확인하고 있는데, 아무런 interaction이 없는데 계속 garbage collection이 일어나는게 아닌가! 이클립스에서 Allocation Tracker를 실행 해 봐야겠다고 맘먹고 분석을 시작! 의외의 결과를 확인하게 되었다.

glGetUniformLocation을 사용하는 부분에서 계속 memory allocation이 발생하고 있었다. 바로 glGetUniformLocation에서 문자열을 동적으로 생성(+ operator 사용)하는 과정에서 String 생성관련 memory allocation 이었다.

어디선가 들었던 "StringBuffer를 사용하세요"라는 얘기가 떠올라 StringBuffer를 사용하도록 코드를 변경 해 보았으나, 줄어들긴 했지만 계속 memory allocation은 발생했다.

결국, index에 따라 동적으로 생성되던 uniform string을 final static으로 변경. memory allocation이 사라졌다!

매 프레임마다 메모리가 할당되는 다른 한 부분이 있는데, 그건 바로 android.opengl.Matrix.invertM method.
호출 할 때마다 80bytes 씩 할당이 된다. inverse matrix 계산은 좀 복잡해서 그냥 android에서 제공하는 method를 사용하고 있는데, 이 마져도 직접 구현을 해야 할까보다.
Posted by 세월의돌
3D그래픽2011. 10. 10. 18:23
Skinning is a technique for deforming geometry by linearly weighting vertices to a set of transformations, represented by <node> elements. Nodes that affect a particular geometry are usually organized into a single hierarchy called a “skeleton,” although the influencing nodes may come from unrelated parts of the hierarchy. The nodes of such a hierarchy represents the “joints” of the skeleton, which should not be confused with the “bones,” which are the imaginary line segments connecting two joints.

This section provides a description of and equations for skinning in COLLADA. 


Overview 

A skinning <controller> associates a geometry with a skeleton. The skeleton is considered to be in its resting position, or bind pose. The bind pose is the world-space position and orientation of each joint when the skeleton was bound to the geometry. This world space is also called the bind-pose space to distinguish it from other world-space coordinate systems. 

A skinning <instance_controller> instantiates a skinning <controller> and associates it with a run-time skeleton. COLLADA defines skinning in object space, so the <instance_controller>’s placement in the <node> hierarchy contributes to the final vertex location. Object-space skinning provides the maximum amount of flexibility. The output of object-space skinning is vertices in the object space of the <node> coordinate system that contains the <instance_controller>

When vertices are skinned in object space, it is easy and efficient to render the same skinned geometry in multiple locations. This is important when multiple actors are displayed simultaneously in the same pose but in different locations. Events like this happen most frequently in the animation of large crowds, parallel machines, and multiactor choreography. Each actor in the same pose shares the same skinned vertex data.  


Skinning Definitions

Definitions related to skinning in COLLADA:

* Bind shape: The vertices of the mesh referred to by the source attribute of the <skin> element.
* Bind-shape matrix: A single matrix that represents the transform of the bind-shape at the time when the mesh was bound to a skeleton. This matrix transforms the bind-shape from object space to bind-space.
* Joints: The bones of a skeleton are defined by their joints; the base of each bone extends to the next joint. In bind space, joints are in their bind pose: the position and orientation at the time the joints of the skeleton were bound to the bind shape. In the <visual_scene>, the joints are oriented according to the poses and animations of the actor. The world-space location of the joints may not directly match the mesh; it is dependent on the root matrix used to convert the mesh back into object-space.
* Weights: How much a joint influences the final destination of a vertex. A vertex is typically weighted to one or more joints, where the sum of the weights equals 1. A vertex is transformed by each joint independently. The multiply transformed vertex results are linearly combined according to their weights to generate the skinned vertex.
* Inverse bind-pose matrix: The inverse of the joint’s bind-space transformation matrix at the time the bind shape was bound to this joint.


Skinning Equations 

The skinning calculation for each vertex v in a bind shape is 

      outv=\sum_{i=0}^{n}\left \{ ((v*BSM)*IBMi*JMi)*JW \right \}

where: 
*  n: The number of joints that influence vertex v
*  BSM: Bind-shape matrix
*  IBMi: Inverse bind-pose matrix of joint i
*  JMi: Transformation matrix of joint i
*  JW: Weight of the influence of joint i on vertex v
Note: v, BSM, IBMi, and JW are constants with regards to some skeletal animation. Depending on your application, it may be beneficial to premultiply BSM with IBMi or v with BSM. 


Equation Notes 

The main difference between world-space skinning and object-space skinning lies in the definition of JMi:
 
* For world-space skinning, JMi is the transformation matrix of the joint from object space to world space.
* For object-space skinning, JMi is a transformation matrix of the joint from object space to another object space. The first object-space transformation is the geometry’s object-space transformation where the bind shape was originally defined. The second object-space transformation is the destination object space, which is selected by the <instance_controller><skeleton>.

It is easiest to conceptualize this transformation by considering the other spaces that may fall between these spaces to construct this final matrix. One method is to go from geometry object space to world space as you might see with world-space skinning, then transform from world space to the skeleton’s object space using the inverse of the skeleton’s world-space matrix. 

It is important to note that the skeleton’s matrix referred to here is not the bind-shape matrix. It is the <node> in the <visual_scene> referenced by <instance_controller><skeleton> and that might not have the same values. Using the <node> referenced by <instance_controller><skeleton> provides maximum flexibility for locating and animating skeletons in the scene. It removes all restrictions over the bind space of the skin and the object space of the skeleton in the scene. This is because the animation is always relative to what you pick as the root node. 

If you were to hypothetically use the bind-shape matrix instead, then the skeleton would always have to be located and animated relative to the bind-shape matrix’s location and orientation in the scene. If you are animating multiple characters at once, this can be disorienting because there is a high probability of overlap. It is worth noting that the node’s world-space matrix, referenced by <instance_controller><skeleton>, can be equal to a skin’s bind-shape matrix and that would match the behavior just mentioned; or it can be equal to an identity matrix to match the behavior of world-space skinning. Enabling these options makes object-space skinning the most flexible model. 

The result of the preceding equation is a vertex in skeleton-relative object space, so it must still be multiplied by a transform from object space to world space to produce the final vertex. This last step is typically done in a vertex shader and this matrix is the world-space transformation matrix for the node that owns the <instance_controller>. 

There is a simple trick to animating a skeleton and its <instance_controller> simultaneously. If you place the <instance_controller> inside the root of <skeleton> then the last two matrices cancel each other, which gives a solution much like world-space skinning. The mesh always follows the skeleton. 

'3D그래픽' 카테고리의 다른 글

The Cg Tutorial  (0) 2011.10.22
onDrawFrame에서의 고려사항 Android OpenGL ES 2.0  (0) 2011.10.22
GLSurfaceView.Renderer interface  (1) 2011.09.29
Build library with android NDK  (0) 2011.09.16
Phong Shading  (0) 2011.09.10
Posted by 세월의돌
3D그래픽2011. 9. 29. 11:41
GLSurfaceView.Renderer interface에는 아래와 같이 세 개의 abstract method가 정의되어 있다.

Public Methods
abstract void onDrawFrame(GL10 gl)
Called to draw the current frame.
abstract void onSurfaceChanged(GL10 gl, int width, int height)
Called when the surface changed size.
abstract void onSurfaceCreated(GL10 gl, EGLConfig config)
Called when the surface is created or recreated.

이름에서 알 수 있듯이, GLSurfaceView.Renderer가 생성될 때 호출되는 순서는 다음과 같고,

onSurfaceCreated() → onSurfaceChanged() → onDrawFrame()


일단 GLSurfaceView.Renderer가 생성되고 나면, surface의 크기가 변경되지 않는 한 onDrawFrame이 반복 호출된다.

그런데, GLSurfaceView에 있는 onPause()가 호출 되면 GLSurfaceView.Renderer interface의 abstract method 들의 호출이 중단되고, onResume()이 호출되면, onSurfaceCreated()부터 다시 호출이 된다. 즉, surface가 다시 생성된다.

Public Methods
void onPause()
Inform the view that the activity is paused.
void onResume()
Inform the view that the activity is resumed.

이렇게 되면, 새로 생성하는 texture나 shader의 index(?)가 모두 0에서부터 다시 시작되는 것으로 보아, 기존에 생성 해 두었던 texture나 shader program 등의 object들이 모두 제거되는 것으로 보인다. 
그러므로, 뭔가 초기화를 하고 싶다면, onPause() / onResume()을 명시적으로 호출하고, onSurfaceCreated()에 필요한 초기화 코드를 추가하면 될 것 같다.

Posted by 세월의돌
3D그래픽2011. 9. 16. 13:18
Bullet Physics를 android에서 사용하기 위해 NDK를 이용하여 build를 하려고 했는데, 수많은 난관이 있었다.

이와 관련하여 몇 가지 정리.


▶ 처음에 빌드를 해 보려고 하는데 STL 관련하여 문제가 있다는것을 발견해서, 이런저런 블로그 글들을 찾아보면서 STLport를 빌드해 보려고 발버둥을 쳤으나 시간낭비. 포기한 상태에서 계속 검색을 하고 있었는데 완전 소중한 link를 발견했다! 바로 android NDK에 STL을 포함한 다양한 부분에서 추가/개선을 해 만들어낸 customized android NDK! 이 NDK를 이용하면 STL 문제없이 빌드가 가능하다.


▶ Bullet Physics를 빌드하려면 libGLESv2.so를 링크해야 하는데, 계속해서 파일을 찾이 못하는 문제가 발생했다. NDK sample에 보면 hello-gl2 라는 예제가 있어서 그것을 가지고 실험(?) 해본 결과, default.properties파일의 유/무에 따라 빌드의 성공 여부가 갈렸다. 이 파일의 내용은 다음과 같다.

# This file is automatically generated by Android Tools.

# Do not modify this file -- YOUR CHANGES WILL BE ERASED!

#

# This file must be checked in Version Control Systems.

#

# To customize properties used by the Ant build system use,

# "build.properties", and override values to adapt the script to your

# project structure.


# Project target.

target=android-8


아마도, target이 지정되지 않으면, 어떤 libGLESv2.so 파일을 사용해야 하는지 명확하지 않아서 발생하는 문제인것 같다.


▶ 아래는 소중한 link를 걸어준 원본 글의 내용이다. : )

You need to add a line of the form:

LOCAL_SHARED_LIBRARIES=libstlport.so

AND you should add libstlport.so to your Application.mk APP_MODULES list, if you want it to be built as part of the same build process.

OR you can do it the easy way and use the Crystax build of the NDK, which makes STL work correctly. See: http://www.crystax.net/android/ndk.php


'3D그래픽' 카테고리의 다른 글

Skin Deformation (or Skinning) in COLLADA (번역)  (0) 2011.10.10
GLSurfaceView.Renderer interface  (1) 2011.09.29
Phong Shading  (0) 2011.09.10
Bézier, Gouraud, Fresnel 의 발음  (0) 2011.09.08
Transforms (변환 행렬)  (0) 2011.09.06
Posted by 세월의돌
3D그래픽2011. 9. 10. 15:36

OpenGL ES 2.0으로 Phone Shading(또는 Per Fragment Lighting)을 구현하기 위해 얼마나 많은 고생을 했는지 몰르겠다.
어디에도 제대로된 sample은 없었다. 오히려 bump mapping 예제가 더 많았던 것 같다.

그런데, 나도 바보같은 것이 그냥 OpenGL sample을 찾아서 수정하겠다고 맘 먹었으면 오히려 쉬웠을 수도 있는데, 내용을 모르다보니 수정하는것에 막연히 겁을 먹었던 것 같기도 하다.

어쨌든 이런 저런 sample들을 받아 이렇게도 해보고 저렇게도 해보아도 제대로 되지 않았던 Phong shading을, "바로 오늘!" 제대로 구현하기에 이르렀다. 물론 이론에 대한 학습이 선행 되었기에 가능했을런지 모르겠다. : )

개인적으로 느낀 한 가지 팁은,
대부분에 해당 될지는 모르겠지만, 처음부터 texture를 사용해서 테스트 하지 않는게 좋겠다.
왜냐하면 texture의 현란한(?) 색상 때문에 정작 구현하고자 하는것이 제대로 보이는지 확인이 어려운 것 같다.
그냥 원색의 단색, 예를들면 빨강(Red)이나 파랑(Blue)을 이용해서 구현하면 좋은것 같다.

Phong Model for Specular Reflection [출처: www.siggraph.org]

Specular reflection is when the reflection is stronger in one viewing direction, i.e., there is a bright spot, called a specular highlight. This is readily apparent on shiny surfaces. For an ideal reflector, such as a mirror, the angle of incidence equals the angle of specular reflection, as shown below.

So if R is the direction of specular reflection and V is the direction of the viewer (located at the View Reference Point or VRP), then for an ideal reflector the specular reflection is visible only when V and R coincide. For real objects (not perfect reflectors) the specular reflectance can be seen even if V and R don't coincide, i.e., it is visible over a range of α values (or a cone of values). The shinier the surface, the smaller the f range for specular visibility. So a specular reflectance model must have maximum intensity at R, with an intensity which decreases as f(α).

Phong Model (by Phong Bui-Tuong)

This is an empirical model, which is not based on physics, but physical observation. Phong observed that for very shiny surfaces the specular highlight was small and the intensity fell off rapidly, while for duller surfaces it was larger and fell off more slowly. He decided to let the reflected intensity be a function of (cos α)n with n >= 200 for a shiny surface and n small for a dull surface. For a perfect reflector n equals infinity, and for a piece of cardboard n equals 0 or 1. In the diagram below we can see how the function (cos α)n behaves for different values of n.

Specular reflection is also a function of the light incidence angle θ. An example is glass which has almost no specular reflectance for θ = 0 degrees but a very high specular reflectance for θ > 80 degrees. Some substances, such as copper, actually change color with change in the incidence angle, as shown in the following plot of the reflectance curve as a function of the incident angle for copper.

 Image Reference

 
A full specular reflectance function is the Bi-directional Reflectance Distribution Function (BRDF). For glass the BRDF at 0 degrees incidence equals 0 and for light incident at 90 degrees, it equals 1. Since for many materials the BRDF is approximately constant, Phong called this term the specular coefficient (ks) and assumed it was constant. Then, since cos α = V·R, a complete illumination intensity model for reflection including diffuse reflection from ambient light and a point light source, and the Phong model for specular reflection is: 
 

I = ka * Ia + (Ip / (d)) [kd * (N\cdot L) + ks * (V\cdot R)^n]

 
For color there will be versions of the above equation for Red, Green, and Blue components. The coefficient of specular reflection ks is usually not the same as the coefficient of diffuse reflection kd or the ambient reflection ka. The assumption is often made that the specular highlights are determined by the color of the light source, not the material, e.g.,  ksR = ksG = ksB = 1.0 This is true of plastic which is why many computer graphics images appear to be plastic.
 
Approximations in the application of the Phong Model

If the point light source is far from the surface then N·L is constant across a planar surface, e.g., across one planar polygon. Similarly if the VRP is far from the surface then V·R is constant across the surface.

● Methods to compute V·R



Implementing Phong Shader (for one Point-Light) [출처: Clockworkcoders Tutorials]

 

varying vec3 N;

varying vec3 v;


void main(void)  

{     

v = vec3(gl_ModelViewMatrix * gl_Vertex);       

N = normalize(gl_NormalMatrix * gl_Normal);

gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;  

}

[Vertex Shader Source Code]

 

varying vec3 N;

varying vec3 v;

void main (void)

{

vec3 L = normalize(gl_LightSource[0].position.xyz - v);

vec3 E = normalize(-v); // we are in Eye Coordinates, so EyePos is (0,0,0)

vec3 R = normalize(-reflect(L,N));

 

//calculate Ambient Term:

vec4 Iamb = gl_FrontLightProduct[0].ambient;

 

//calculate Diffuse Term:

vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0);

Idiff = clamp(Idiff, 0.0, 1.0);

 

// calculate Specular Term:

vec4 Ispec = gl_FrontLightProduct[0].specular * pow(max(dot(R,E),0.0), 0.3 * gl_FrontMaterial.shininess);

Ispec = clamp(Ispec, 0.0, 1.0);


// write Total Color:

gl_FragColor = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec;

}

 

[Fragment Shader Source Code]
우선 Vertex Shader Source Code에서,
gl_ModelViewMatrix, gl_ModelViewProjectionMatrix, gl_NormalMatrix, gl_Vertex, gl_Normal 은 OpenGL ES 2.0에는 없는 built-in variable 이기 때문에, 따로 uniform 및 attribute로 추가해 주어야 한다.

다음으로 Fragment Shader Source Code에서도,
gl_LightSource, gl_FrontLightProduct, gl_FrontMaterial, gl_FrontLightModelProduct 역시 OpenGL ES 2.0에는 없는 build-in variable 이므로, 따로 uniform으로 추가 해 주어야 한다.

[2011.10.03 추가]
* 처음 Phong Shading 구현에 성공했을 당시(거의 한달 전-_-;)에는, 그 자제가 너무 감격스러워 lighting 이라는 본질에 대해서는 망각하고 있었다. 즉, lighting의 방향에 대해서는 제대로 확인조차 하지 않았던 것이다. 그리고 이런 저런 정리가 어느정도 된 상태에서 확인을 해 보니, 아무리 조명의 위치를 변경해도 항상 제자리에서 꿈쩍도 하지 않았던 것! ㅜ.ㅜ
  한참을 고민해 봐도 모든게 sample code와 다르지 않았고, 급기야 그림을 그리며 directional light와 Phong shading에 대해 공부하기에 이르렀지만, 모든게 정상적이었고 되지 않을 이유가 없었는데... Shader에서 문제(?)가 생기니, 정말 막막하기만 했다.
  Back to Basics! 기본으로 돌아가서 다시 하나하나 점검을 하던 중, 이상한 것을 발견! 그건 바로 light position의 data type이 vec4 로 되어 있었는데, client code에서는 glUniform3f를 사용한 것이었다. 이런 된장! -_-; 주의하자;;

* 여러종류 light 다수를 하나의 shader에 구현하려고 했는데, 그러지 말란다. 실제로도 shader code에 control이 들어가면 성능이 급격히 떨어진다. 아직도 갈 길은 멀었으니...;;

'3D그래픽' 카테고리의 다른 글

GLSurfaceView.Renderer interface  (1) 2011.09.29
Build library with android NDK  (0) 2011.09.16
Bézier, Gouraud, Fresnel 의 발음  (0) 2011.09.08
Transforms (변환 행렬)  (0) 2011.09.06
Goraud shading vs. Phong shading  (0) 2011.09.02
Posted by 세월의돌
3D그래픽2011. 9. 8. 08:52
이전부터 Gouraud Shading을 "고라우드"라고 발음했었는데, 어떤 책을 보면 "고로"로 표시되어 있기도하고...

이번에 직접 이것저것 구현 해 보면서 실제 발음이 궁금했다.

그래서 검색하다 보니, 불어라는 것을 알게 되었고, 친절하게 audio clip까지 posting 되어있는 웹페이지를 발견.

http://www.realtimerendering.com/blog/bzier-gouraud-fresnel/
(Audio Clip direct link - BezierGouraudFresnel.mov)

고등학교 불어수업 시간을 상기해 보면, 한글로 표현할 수 있는 발음은 "구호"라고 하면 될것 같은데, "구로"와 "구호"의 중간 발음이라고 생각하면 될 것 같다.

'3D그래픽' 카테고리의 다른 글

Build library with android NDK  (0) 2011.09.16
Phong Shading  (0) 2011.09.10
Transforms (변환 행렬)  (0) 2011.09.06
Goraud shading vs. Phong shading  (0) 2011.09.02
Normal Transform(법선벡터 변환)  (0) 2011.09.02
Posted by 세월의돌
3D그래픽2011. 9. 6. 21:22
▶ 평행이동 (Translation)

T(t)=\left ( \begin{array}{cccc} 1 & 0 & 0 & t_{x}\\ 0 & 1 & 0 & t_{y}\\ 0 & 0 & 1 & t_{z}\\ 0 & 0 & 0 & 1\\ \end{array} \right )

→ 평행이동의 역변환

T^{-1}(t)=T(-t)=\left ( \begin{array}{cccc} 1 & 0 & 0 & -t_{x}\\ 0 & 1 & 0 & -t_{y}\\ 0 & 0 & 1 & -t_{z}\\ 0 & 0 & 0 & 1\\ \end{array} \right )



▶ 회전 (Rotation)

R_{x}(\phi)=\left ( \begin{array}{cccc} 1 & 0 & 0 & 0\\ 0 & cos\phi & -sin\phi & 0\\ 0 & sin\phi & cos\phi & 0\\ 0 & 0 & 0 & 1\\ \end{array} \right )
R_{y}(\phi)=\left ( \begin{array}{cccc} cos\phi & 0 & sin\phi & 0\\ 0 & 1 & 0 & 0\\ -sin\phi & 0 & cos\phi & 0\\ 0 & 0 & 0 & 1\\ \end{array} \right )
R_{z}(\phi)=\left ( \begin{array}{cccc} cos\phi & -sin\phi & 0 & 0\\ sin\phi & cos\phi & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1\\ \end{array} \right )


→ 모든 3x3 행렬 R에서 대각 원소들의 합 tr(R)은 축과 무관하게 상수값을 가진다.

tr(R)=1+2cos\phi

이 합을 trace라고 부른다.


→ 모든 회전행렬은 직교 행렬이므로

R^{-1}=R^{T}

과 같이, 역행렬은 전치행렬과 같다. 역행렬을 구하는 다른 방법은

R_{i}^{-1}(\phi)=R_{i}(-\phi)

이다.


→ 회전행렬의 판별자는 항상 1개 뿐이다.


→ (예) 특정 점 p를 중심으로 z축을 따라 φ라디안 만큼 물체를 회전시키는 변환 행렬은
(-p) 만큼 평행이동 → z축으로 φ회전 → (p) 만큼 평행이동 시키는 것이다.

X=T(p)R_{z}(\phi)T(-p)



▶ 크기 조정 (Scaling)

S(s)=\left ( \begin{array}{cccc} s_{x} & 0 & 0 & 0\\ 0 & s_{y} & 0 & 0\\ 0 & 0 & s_{z} & 0\\ 0 & 0 & 0 & 1\\ \end{array} \right )

단, s_{x}=s_{y}=s_{z} 인경우 uniform(isotropic)이라 하고, 그렇지 않은경우 non-uniform(anisotropic)이라 한다.

역변환은
S^{-1}(s)=S(\frac{1}{s_{x}}, \frac{1}{s_{y}}, \frac{1}{s_{z}})

특히, uniform이고 동차좌표를 이용하는 경우에는 w 성분을 이용하여 역변환을 표현할 수 있다.

S(s)=\left ( \begin{array}{cccc} s & 0 & 0 & 0\\ 0 & s & 0 & 0\\ 0 & 0 & s & 0\\ 0 & 0 & 0 & 1\\ \end{array} \right )=\left ( \begin{array}{cccc} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & \frac{1}{s}\\ \end{array} \right )

→ 성분들 가운데 하나 또는 세 개가 음수이면 거울 행렬(mirror matrix)이라고도 불리는 반사 행렬(reflection matrix)이 만들어진다. 이렇게 되면 정점들의 감기 순서(winding order)가 반대로 바뀐다.

좌측 상단 3x3 행렬의 판별자의 값이 음수이면 주어진 행렬이 반사 행렬이다.


▶ 변환의 결합

변환의 결합은 순서가 중요하며 다음과 같은 순서로 결합한다.

C=TRS

TRSp=(T(R(Sp)))

'3D그래픽' 카테고리의 다른 글

Phong Shading  (0) 2011.09.10
Bézier, Gouraud, Fresnel 의 발음  (0) 2011.09.08
Goraud shading vs. Phong shading  (0) 2011.09.02
Normal Transform(법선벡터 변환)  (0) 2011.09.02
COLLADA (3D Asset Exchange Schema)  (0) 2011.08.31
Posted by 세월의돌
3D그래픽2011. 9. 2. 15:24
Phong Shading 이란 용어는 그래도 어느정도 들어봤는데, 이와 관련된 내용을 찾다보면 per-pixel lighting 이라던지 per-fragment lighting 이라는 용어가 계속 튀어 나온다. 그래서 내가 뭔가 잘못 알고 있는건가 고민하다가 찾아보기로 결심!

Wikipedia의 내용으로 정리 해 본다.

Gouraud shading, named after Henri Gouraud, is an interpolation method used in computer graphics to produce continuous shading of surfaces represented by polygon meshes. In practice, Gouraud shading is most often used to achieve continuous lighting on triangle surfaces by computing the lighting at the corners of each triangle and linearly interpolating the resulting colours for each pixel covered by the triangle. Gouraud first published the technique in 1971.
  
OpenGL ES 1.x의 fixed pipeline에 구현되어 있는 것으로 알 고 있다. Goraud shading은 vertex에 대해서 연산을 수행하고, 각 pixel(fragment)에 대해서는 단순히 linear interpolation을 하기 때문에, 왼쪽 그림과 같이 polygon의 수가 적은경우 어색할 수 밖에 없다. 다만 1971년에 나온 기술이라니, 그 당시 이러한 연구를 했다는 것 자체가 너무 신기하다.


Phong shading refers to an interpolation technique for surface shading in 3D computer graphics. It is also called Phong interpolationnormal-vector interpolation shadingper-fragment lighting or per-pixel-based lighting. Specifically, it interpolates surface normals across rasterized polygons and computes pixel colors based on the interpolated normals and a reflection model. Phong shading may also refer to the specific combination of Phong interpolation and the Phong reflection model.
결과적으로 Phong shading과 per-pixel lighting 등등은 모두 동일한 용어! : )
normal을 interpolation해서 pixel(또는 fragment)마다 연산을 수행하는 것이 특징. 당연히 더욱 부드럽게 보일 수 있지만 연산량은 Goraud shading에 비해 엄청나게 많아질 수 밖에 없다.



이제 궁금증을 해결 했으니 다시, shader coding의 세계로...

'3D그래픽' 카테고리의 다른 글

Bézier, Gouraud, Fresnel 의 발음  (0) 2011.09.08
Transforms (변환 행렬)  (0) 2011.09.06
Normal Transform(법선벡터 변환)  (0) 2011.09.02
COLLADA (3D Asset Exchange Schema)  (0) 2011.08.31
OpenGL ES 2.0 Q&A by myself  (5) 2011.07.12
Posted by 세월의돌
3D그래픽2011. 9. 2. 14:44
Per-Fragment Lighting을 구현하려고 이것 저것 자료를 보다가, 생소한 용어들을 만나게 되어 좀 찾아 보았다.
생소한 용어들이란 바로 binormal &  tangent vector 이다.

* Binormal vector - normal에 수직인 vector (이것은 dot product를 이용한 방정식으로 구할 수 있을 듯)
* Tangent vector - normal과 binormal에 동시에 수직인 vector (달리 말하면 normal과 binormal이 이루는 평면에 수직인 vector 또는 그 평면의 normal, 이것은 normal과 binormal의 cross product로 구할 수 있다)

이와 관련해서 다시 책(Real-time Rendering)에서 찾아보니, 책의 초반부에 Normal Transform과 관련된 내용이 있었다.

법선벡터들은 기하구조 변환에 사용된 특정 행렬의 역행렬의 전치 행렬에 의해서 변환되어야만 한다. 그러므로 기하 구조를 변환하는 데 사용한 행렬이 M 이라면 그 기하 구조의 법선 벡터를 변환하는 데는 다음과 같은 행렬 N을 사용하여야 한다.

N = (M^{-1})^{T}

참고로 직교 행렬의 역행렬은 자신의 전치 행렬이므로 그 행렬 자체가 법선 벡터를 변환하는데 바로 사용될 수 있다.
원래의 회전 행렬이 주어지면 두 개의 행렬 전치는 소거된다.
더욱이 평행 이동은 벡터 방향에 영향을 주지 않으므로 평행 이동을 몇 번 하든 법선 벡터에는 영향을 주지 않는다.

역시, Real-Time Rendering은 아무나 보는것이 아니었나보다.
이런 내용이 무려 p.55에 등장한다. 까만것은 글씨요 하얀것은 종이 였으리라... oTL
이제 다시 보니, 어렴풋이 뭔가 그려지는 정도? 그러나 아직 멀었다. : )

'3D그래픽' 카테고리의 다른 글

Transforms (변환 행렬)  (0) 2011.09.06
Goraud shading vs. Phong shading  (0) 2011.09.02
COLLADA (3D Asset Exchange Schema)  (0) 2011.08.31
OpenGL ES 2.0 Q&A by myself  (5) 2011.07.12
OpenGL ES 2.0 on Android 2.2  (0) 2011.06.29
Posted by 세월의돌
3D그래픽2011. 8. 31. 18:22
Khronos Group Logo


static mesh는 <library_visual_scenes>에 정의된 <node>들을 이용하여 렌더링을 하게 된다.


 static mesh를 구분하는 방법<library_visual_scenes>의 child node인 <node> element의 type attribute가 JOINT가 아닌지 확인이 필요(<node> element의 type attribute의 기본값은 NODE이며, 없어도 되는 optional attribute)하고, <node>의 child element에 <instance_geometry> element가 존재해야 한다.


 <library_geometries>는 <library_visual_scenes>에서 참조하는 모든 Vertices, Normals, Texcoords(Texture Coordinates) 그리고 이들을 참조하여 삼각형들을 구성할 수 있는 indices를 포함하고 있다.


 (2011.08.26) <node> element에는 child element로 <matrix>를 가지고 있고, 이것은 해당 node의 World Matrix를 나타낸다.

<node name="Teapot001" id="Teapot001" sid="Teapot001">

<matrix sid="matrix">

1.000000 0.000000 0.000000 0.000000

0.000000 -0.000000 1.000000 -7.700000

0.000000 -1.000000 -0.000000 -0.000000

0.000000 0.000000 0.000000 1.000000

</matrix>

</node>


OpenGL에서는 보이는 형식 그대로 float array를 만들면 되고, DirectX에서는 전치(transpose)를 해야 한다고 한다. 그런데 이것도 항상 그런것은 아닌듯. Platform에 따라 다른것 같다. Android에서는 OpenGL을 사용함에도 불구하고 Matrix class의multiplyMM을 이용하려면 전치(transpose)를 해야 한다! 혹시 Android 개발자들 중에 DirectX에 익숙한 사람이 있었던 것일까? :(

(2011.10.06 추가) 의문이 풀렸다. COLLADA Spec.에서 아래와 같은 문장을 발견. 모든것엔 이유가 있다.

It is written in row-major order in the COLLADA document for human readability.




 (2011.08.31) Collada의 Spec.이 그런건지, modeling tool들이 그런건지 아직 확실하지 않지만, Android의 텍스쳐의 좌표계가 다른 것 같다. 즉, 뒤집어져 출력 된다. 어떻게 해결해야 할지는 다시 고민해 봐야겠다.

찾아보니 보통 DirectX의 텍스쳐 좌표계를 OpenGL(또는 Android) 텍스쳐 좌표계로 변환할 때, 다음과 같이 한다고 한다.

glMatrixMode(GL10.GL_TEXTURE);

glPushMatrix();

glLoadIdentity();

glScalef(1.0f, -1.0f, 1.0f);


이것은 (s, t) 좌표 중 t를 반대로 뒤집어 주는 것. 즉, 상하 반전을 시켜주는 효과!
그래서 실제 데이터를 추출하는 과정에서 t 값에 -1을 곱해 주었더니 제대로 나온다.
그런데, 문제는 model data 마다 다를 수 도 있을것 같다는 사실...

어떻게 알아낼 수 있을까?

3DS Max에서 출력해 주는 데이터는 제대로 되어 있을까?

Collada Spec.에서 (s, t) 좌표계의 종류를 나타내는 property는 못 본것 같은데...


 (2011.08.31) Triangle Mesh라는 개념이 궁금하다.
Bullet Physics Simulation User Manual 에 보면, Triangle Mesh는 vertices data와 indices로 구성된다고 하는데...
Triangle Mesh는 이래야 한다는 어떤 정의나 관습 같은게 있는걸까? 언젠가는 알 수 있겠지: )


 (2011.08.31) Tangent and Bi-Tangent for each vertex? 이건 뭐하는데 사용되는 걸까?
[2011.09.30] 이건 bump mapping으로 lighting을 표현할 때 필요한 요소! 좀더 사실적인 깊이 표현을 위해 texture와 그 texture에 맞는 적절한 normal map(이 또한 texture)을 이용하여 fragment shader에서 빛을 계산하게 된다.


 (2011.08.31) 이런 고민도 된다. 결과적으로 성능 이슈때문에 NDK로 다시 개발해야 하지 않을까 라는... 과연? : (
[2011.09.30] NDK로 다시 개발하기 보다는 필요한 data format을 binary로 정의하고, 이 format에 맞도록 offline으로 데이터를 추출하여 저장하는 방식으로 하면 될 것 같다.


 (2011.09.30) COLLADA의 <light> element spec.을 보면, <color> element  하나만 정의되어 있는데, 보통은 light도 ambient / diffuse / specular term을 모두 가지고 있다. 왜 이러한 mismatch가 발생하는지 궁금했었다.
[2011.09.30] 처음에는 <color> element가 하나만 있으므로, 당연히 diffuse term을 계산할 때 사용해야 한다고 생각했으나, spec.에서 <phong> element 부분을 보다가, 다음과 같은 식을 발견하면서 (내 마음대로) 한 가지 사실을 알게 되었다.

<color>=<emission>+<ambient>*al+<diffuse>*max(N•L,0)+<specular>*max(R•I,0)shineness

where:
• al – A constant amount of ambient light contribution coming from the scene. In the COMMON profile,
this is the sum of all the <light><technique_common><ambient><color> values in the <visual_scene>.

• N – Normal vector
• L – Light vector
• I – Eye vector
• R – Perfect reflection vector (reflect (L around N))


즉, al은 <light> element의 <color> 값을 의미하며, 이것은 ambient term을 계산할 때 사용한다는 것이다.
여기서 한가지 헷갈리는 점은, spec.에 명시적으로 ambient light의 <color>로 되어 있다는 것! ambient light 이기때문에 ambient term에 적용한다는 것인지, 예를 들어 설명한 것인지 모르겠다. directional / point / spot light 모두 <color> element는 하나만 있기 때문에 동일한 것이 아닌가란 생각이 들었다.

그런데 또 다른 의문점은, 그렇다면 백색광원이 아닌 유색광원의 경우, diffuse term에 적용해야 할 텐데 이런경우에 대한 COLLADA sample을 보지 못해서, 어떻게 표현해야 할지 난감. 우선은 이대로 진행한다 -_-;


 (2011.10.03) Light Spec.에서의 특이점(또는 암시적 가정-_-;)
* <ambient> - It defines a single <color>  element that contains the RGB color value for the light. The ambient color value can be targeted for animation as discussed in Chapter 6, “COLLADA Animations,” page 121. The ambient light is not affected by such spatial transformations since it radiates omnidirectionally from everywhere in the scene equally.

* <directional> - Its type implicitly means that it has a direction vector that shines the light down its local negative Z-axis. This direction can be reoriented by rotating the light when it is placed in the scene.

* <point> - The location of the point light is established when it is placed in the scene. For example, here we place a point light at the local coordinates X=50, Y=30, and Z=20 using a <translate> element.

* <spot> - The <spot>  element has an implied direction facing down the negative Z-axis, just like the <directional> element. The actual orientation is established when the light is placed in the scene, as with the other light types. The value of <falloff_angle> can range from 0 to 90 degrees, or it can have the special value of 180 degrees for a uniform distribution. The value of <falloff_exponent> can range from 0 to 128. Higher exponent values focus the beam.

즉, 기본적으로 방향이 있는 모든 조명은 -Z 축을 향하고 있다... 는 얘기!


 (2011.10.05) 각각의 <node>는 각각의 local coordinate system을 정의한다. 그러므로 <node>에서 변환에 의해 정해지는 local coordinate system은 view transformation에 의해 world coordinate system으로 옮겨줘야 하는 것 같다. (...?)


 (2011.10.04) 관련 링크

 (2011.10.06) COLLADA에서 읽어 들이는 data는 실제로는 Joint. Bone은 두 개의 Joint를 연결하는 가상의 선이라고 생각 하면 된다.


 (2011.10.09) Skeletal Animation 정보 <library_controllers> -> <controller> -> <skin>
(1) Save <bind_shape_matrix> element를 읽어 저장한다
(2) <vertex_weights> element를 읽는다
 - 이 개수는 geometry mesh의 vertices의 개수와 동일하다
 - <vcount> element의 value의 개수는 <vertex_weights> element의 "count" attribute의 값과 동일하므로 참고
 - <vcount> element의 각각의 값은, 탐색중인 특정 vertex에 영향을 미치는 Joint의 개수를 의미


'3D그래픽' 카테고리의 다른 글

Transforms (변환 행렬)  (0) 2011.09.06
Goraud shading vs. Phong shading  (0) 2011.09.02
Normal Transform(법선벡터 변환)  (0) 2011.09.02
OpenGL ES 2.0 Q&A by myself  (5) 2011.07.12
OpenGL ES 2.0 on Android 2.2  (0) 2011.06.29
Posted by 세월의돌