펜 툴로 도형을 만들 수 있게 되었으니 실제로 3d로 만들 차례이다.
그래서 그린 도형을 튀어나오게 만드는 extrude 기능을 만들었다.
Extrude
class Extrude:public IState, public IPressedDown ,public IPressed, public IPressedUp{
public:
void Handle() override;
void HandleOut() override;
void OnPointerDown(float xpos, float ypos,float xdelta,float ydelta) override;
void OnPointer(float xpos, float ypos,float xdelta,float ydelta) override;
void OnPointerUp(float xpos, float ypos,float xdelta,float ydelta) override;
private:
bool bState=false;
std::vector<Vertex> mCurrentVertices;
std::vector<Vertex> mNewVertices;
std::vector<Vertex> mVertices;
void selectFace();
void extrudeMesh(float xdelta, float ydelta);
};
void Extrude::extrudeMesh(float xdelta, float ydelta){
if(glm::length(mNewVertices[0].Position-mCurrentVertices[0].Position)<=0.0f){
return;
}
Mesh* mesh = Collection::GetInstance()->GetSelectedMesh();
glm::vec3 faceNormal = glm::normalize(glm::cross(
mCurrentVertices[1].Position-mCurrentVertices[0].Position,
mCurrentVertices[2].Position-mCurrentVertices[1].Position));
for(int i=0;i<mNewVertices.size();i++){
mNewVertices[i].Position.x = (mNewVertices[i].Position.x -(faceNormal.x*((ydelta+xdelta)/2))*0.03f);
mNewVertices[i].Position.y = (mNewVertices[i].Position.y -(faceNormal.y*((ydelta+xdelta)/2))*0.03f);
mNewVertices[i].Position.z = (mNewVertices[i].Position.z -(faceNormal.z*((ydelta+xdelta)/2))*0.03f);
}
std::vector<Vertex> resultVertices = mCurrentVertices;
std::vector<Vertex>::iterator it = resultVertices.end();
resultVertices.insert(it,mNewVertices.begin(),mNewVertices.end());
unsigned int vertexNum = (mCurrentVertices.size()/3)+2;
mVertices[vertexNum]=mNewVertices[0];
mVertices[vertexNum+1]=mNewVertices[1];
mVertices[vertexNum+2]=mNewVertices[2];
for(int i=1;i<vertexNum-2;i++){
mVertices[vertexNum+2+i]=mNewVertices[3*i+2];
}
for(int i=0;i<vertexNum;i++){
resultVertices.push_back(mVertices[i]);
resultVertices.push_back(mVertices[i+vertexNum]);
resultVertices.push_back(mVertices[(i+1)%vertexNum+vertexNum]);
resultVertices.push_back(mVertices[i]);
resultVertices.push_back(mVertices[(i+1)%vertexNum+vertexNum]);
resultVertices.push_back(mVertices[(i+1)%vertexNum]);
}
mesh->vertices = resultVertices;
mesh->SetMesh();
}
extrudeMesh는 말 그대로 기존의 메시를 튀어나오게 만든다.
기존에 그려져있던 버텍스를 복사해서 새로운 면을 만들고, 옆면을 만들어주는 버텍스들을 채운 다음에 메시를 만든다.
노말값 채우기
위 짤에서 보다시피, 아직 버텍스에 위치만 있고 노말이 없어서 면 구분이 안된다.
그래서 버텍스에 위치와 노말도 채워넣어야 한다.
그런데 펜툴과 extrude 기능은 그냥 만들었는데..
버텍스 노말을 계산하는 것은 쉽지않았다.
그 이유는 버텍스 노말을 어떻게 채워야 할지 잘 몰랐기 때문이다.
페이스 노말은 짤에서 보다시피 CB CA와 같은 두 벡터를 외적하면 얻을 수 있다.
그러면 버텍스 노말은?
나는 인접한 면의 페이스 노말을 모두 더해서 정규화하면 버텍스 노말이 나올 거라고 생각했다.
그래서 그렇게 구현하면?
void Extrude::setVertexNormal(){
unsigned int vertexNum = (mCurrentVertices.size()/3)+2;
std::vector<std::vector<unsigned int>> faces;
std::vector<glm::vec3> facesNormal;
glm::vec3 faceNormal = glm::cross(
mVertices[1].Position-mVertices[0].Position,
mVertices[2].Position-mVertices[1].Position);
std::vector<unsigned int> upFace;
for(int i=0;i<vertexNum;i++){
upFace.push_back(i);
}
faces.push_back(upFace);
facesNormal.push_back(faceNormal);
std::vector<unsigned int> downFace;
for(int i=vertexNum;i<vertexNum*2;i++){
downFace.push_back(i);
}
faces.push_back(downFace);
facesNormal.push_back(glm::vec3(-faceNormal.x,-faceNormal.y,-faceNormal.z));
for(int i=0;i<mNewVertices.size();i++){
mNewVertices[i].Position.x = (mNewVertices[i].Position.x -(faceNormal.x*0.1f));
mNewVertices[i].Position.y = (mNewVertices[i].Position.y -(faceNormal.y*0.1f));
mNewVertices[i].Position.z = (mNewVertices[i].Position.z -(faceNormal.z*0.1f));
}
mVertices[vertexNum]=mNewVertices[0];
mVertices[vertexNum+1]=mNewVertices[1];
mVertices[vertexNum+2]=mNewVertices[2];
for(int i=1;i<vertexNum-2;i++){
mVertices[vertexNum+2+i]=mNewVertices[3*i+2];
}
for(int i=0;i<vertexNum;i++){
std::vector<unsigned int> face;
face.push_back(i);
face.push_back(i+vertexNum);
face.push_back((i+1)%vertexNum+vertexNum);
face.push_back((i+1)%vertexNum);
faces.push_back(face);
facesNormal.push_back(glm::cross(
mVertices[(i+1)%vertexNum].Position-mVertices[i].Position,
mVertices[i+vertexNum].Position-mVertices[i].Position
));
}
for(int index=0;index<mCurrentVertices.size();++index){
glm::vec3 sum = glm::vec3(0.0f);
int k;
if (index % 3 == 0) {
k=0;
} else {
k=index / 3 + index % 3;
}
for(int i=0;i<faces.size();i++){
for(int j=0;j<faces[i].size();j++){
if(faces[i][j]==k){
sum+=facesNormal[i];
break;
}
}
}
mCurrentVertices[index].Normal= glm::normalize(sum);
}
for(int index=0;index<mNewVertices.size();++index){
glm::vec3 sum = glm::vec3(0.0f);
int k;
if (index % 3 == 0) {
k=0;
} else {
k=index / 3 + index % 3;
}
for(int i=0;i<faces.size();i++){
for(int j=0;j<faces[i].size();j++){
if(faces[i][j]==k+vertexNum){
sum+=facesNormal[i];
break;
}
}
}
mNewVertices[index].Normal= glm::normalize(sum);
}
}
코드는.. 복잡한데
어쨌든 해당 버텍스에 인접한 면을 구하고
인접한 면의 페이스 노말을 다 더해서 정규화해서
해당 버텍스의 노말을 구했다.
그래서 결과는..
아직 조명은 없기 때문에 노말을 바로 출력했다.
그런데.. 뭔가 딱봐도 이상하다
상식적으로 면 별로 노말값이 같아야 할 것 같은데 그렇지 않다.
그래서 실제 3d 모델들은 어떻게 버텍스 노말을 구성하나..
learnopengl 사이트에서 제공하는 기본 큐브를 분석해봤더니..
https://learnopengl.com/code_viewer.php?code=lighting/basic_lighting_vertex_data
지금까지 버텍스의 위치가 같으면 노말값도 똑같을 거라고 생각했는데
버텍스의 위치가 같아도 노말값이 다르다.
자기가 나타내고 있는 면의 노말값을 가지고 있다.
좀 생각해보니깐 당연한 것 같기도 하고..
그래서 다시 수정..
void Extrude::extrudeMesh(float xdelta, float ydelta){
if(glm::length(mNewVertices[0].Position-mCurrentVertices[0].Position)<=0.0f){
return;
}
Mesh* mesh = Collection::GetInstance()->GetSelectedMesh();
glm::vec3 faceNormal = glm::normalize(glm::cross(
mCurrentVertices[1].Position-mCurrentVertices[0].Position,
mCurrentVertices[2].Position-mCurrentVertices[1].Position));
for(int i=0;i<mNewVertices.size();i++){
mNewVertices[i].Position.x = (mNewVertices[i].Position.x -(faceNormal.x*((ydelta+xdelta)/2))*0.03f);
mNewVertices[i].Position.y = (mNewVertices[i].Position.y -(faceNormal.y*((ydelta+xdelta)/2))*0.03f);
mNewVertices[i].Position.z = (mNewVertices[i].Position.z -(faceNormal.z*((ydelta+xdelta)/2))*0.03f);
mNewVertices[i].Normal=faceNormal;
}
std::vector<Vertex> resultVertices = mCurrentVertices;
std::vector<Vertex>::iterator it = resultVertices.end();
resultVertices.insert(it,mNewVertices.begin(),mNewVertices.end());
unsigned int vertexNum = (mCurrentVertices.size()/3)+2;
mVertices[vertexNum]=mNewVertices[0];
mVertices[vertexNum+1]=mNewVertices[1];
mVertices[vertexNum+2]=mNewVertices[2];
for(int i=1;i<vertexNum-2;i++){
mVertices[vertexNum+2+i]=mNewVertices[3*i+2];
}
for(int i=0;i<vertexNum;i++){
Vertex vert = {glm::vec3(1.0f),mFacesNormal[i+2],glm::vec2(1.0f),glm::vec3(1.0f)};
vert.Position = mVertices[i].Position;
resultVertices.push_back(vert);
vert.Position = mVertices[i+vertexNum].Position;
resultVertices.push_back(vert);
vert.Position = mVertices[(i+1)%vertexNum+vertexNum].Position;
resultVertices.push_back(vert);
vert.Position = mVertices[i].Position;
resultVertices.push_back(vert);
vert.Position = mVertices[(i+1)%vertexNum+vertexNum].Position;
resultVertices.push_back(vert);
vert.Position = mVertices[(i+1)%vertexNum].Position;
resultVertices.push_back(vert);
}
mesh->vertices = resultVertices;
mesh->SetMesh();
}
요약하자면 그냥 이상한 계산하지말고
해당 면의 노말을 가지도록 했다.
이렇게하면..
되는거 맞겠지??
조명
버텍스 노말도(아마)계산했으니
기본적인 조명도 구현했다.
쉐이더 코드는 대충 learnopengl에서 긁어왔고
ambient+diffuse+specular 라이트가 구현되어있다.
'컴퓨터 그래픽스 > OpenGL' 카테고리의 다른 글
#9 Tinylender 개발일지 : TextBox 개발 및 인스펙터 (3) | 2024.07.28 |
---|---|
#8 Tinylender 개발일지 : 레이어 개발(1) (0) | 2024.07.25 |
#6 tinylender 개발일지 : 펜툴 제작 (0) | 2024.05.31 |
#5 tinylender 개발일지 : 상태 머신 적용 (0) | 2024.05.12 |
#4 gulender 개발일지 : mesh, model 그리고 assimp 세팅 (0) | 2024.05.12 |