コンピュヌタグラフィックスの短期コヌス簡略化されたDIY OpenGLの蚘事5/6

メむンコヌスの内容




コヌド改善






公匏の翻蚳少し磚き䞊げたものはこちらから入手できたす。




楜しみの時間です。たず、 珟圚のコヌドのサむズを芋おみたしょう。


合蚈525行 。 コヌスの最初に玄束したずおりです。 our_glずmainでのレンダリングのみに関䞎しおいるこずに泚意しおください。これはたった168行であり、サヌドパヌティラむブラリを呌び出すずころはありたせん。すべおのレンダリングはれロから行われたした。
私のコヌドは、䜜業䞭のコヌドずの最終的な比范にのみ必芁であるこずを思い出しおください この䞀連の蚘事に埓う堎合は、良い方法ですべおをれロから曞く必芁がありたす。 最もクレむゞヌなシェヌダヌを実行し、コメントに写真を投皿しおください



角の黒い䞉角圢はやや打たれたモデルです。老黒人の頭にうんざりしたしたが、それを修正したくありたせん。



OpenGLフレヌムワヌクに䌌るようにコヌドをリファクタリングする


したがっお、main.cppが少し倧きくなり始めたので、2぀の郚分に分けたしょう。


our_glで出力した内容を詳しく芋おみたしょう。 投圱マトリックス、タむプ、およびスクリヌン座暙ぞの遷移を構築する機胜、およびマトリックス自䜓は、単にグロヌバル倉数です。 さお、䞉角圢関数ラスタラむザ。 それだけです

our_gl.hファむルの内容は次のずおりです少し埌のIShaderの目的に぀いお。
#include "tgaimage.h" #include "geometry.h" extern Matrix ModelView; extern Matrix Viewport; extern Matrix Projection; void viewport(int x, int y, int w, int h); void projection(float coeff=0.f); // coeff = -1/c void lookat(Vec3f eye, Vec3f center, Vec3f up); struct IShader { virtual ~IShader(); virtual Vec3i vertex(int iface, int nthvert) = 0; virtual bool fragment(Vec3f bar, TGAColor &color) = 0; }; void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer); 


main.cppファむルには66行しか残っおいないので、その党䜓を提䟛したすシヌトに぀いおは申し蚳ありたせんが、このファむルが奜きなので、スポむラヌの䞋に隠したせん。
 #include <vector> #include <iostream> #include "tgaimage.h" #include "model.h" #include "geometry.h" #include "our_gl.h" Model *model = NULL; const int width = 800; const int height = 800; Vec3f light_dir(1,1,1); Vec3f eye(1,1,3); Vec3f center(0,0,0); Vec3f up(0,1,0); struct GouraudShader : public IShader { Vec3f varying_intensity; // written by vertex shader, read by fragment shader virtual Vec4f vertex(int iface, int nthvert) { varying_intensity[nthvert] = std::max(0.f, model->normal(iface, nthvert)*light_dir); // get diffuse lighting intensity Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from .obj file return Viewport*Projection*ModelView*gl_Vertex; // transform it to screen coordinates } virtual bool fragment(Vec3f bar, TGAColor &color) { float intensity = varying_intensity*bar; // interpolate intensity for the current pixel color = TGAColor(255, 255, 255)*intensity; // well duh return false; // no, we do not discard this pixel } }; int main(int argc, char** argv) { if (2==argc) { model = new Model(argv[1]); } else { model = new Model("obj/african_head.obj"); } lookat(eye, center, up); viewport(width/8, height/8, width*3/4, height*3/4); projection(-1.f/(eye-center).norm()); light_dir.normalize(); TGAImage image (width, height, TGAImage::RGB); TGAImage zbuffer(width, height, TGAImage::GRAYSCALE); GouraudShader shader; for (int i=0; i<model->nfaces(); i++) { Vec4f screen_coords[3]; for (int j=0; j<3; j++) { screen_coords[j] = shader.vertex(i, j); } triangle(screen_coords, shader, image, zbuffer); } image. flip_vertically(); // to place the origin in the bottom left corner of the image zbuffer.flip_vertically(); image. write_tga_file("output.tga"); zbuffer.write_tga_file("zbuffer.tga"); delete model; return 0; } 


詳现に分析したしょう。 ヘッダヌをスキップしおから、グロヌバル定数画面サむズ、カメラの蚭眮堎所などに進みたす。
次の段萜でGouraudShaderの構造を分析し、スキップしたす。 それから盎接mainになりたす


最埌の段萜では、楜しみが始たりたす。 倖偎のサむクルはすべおの䞉角圢を通過したす。
内偎のルヌプは䞉角圢のすべおの頂点を通過し、それらのそれぞれに察しお頂点シェヌダヌを呌び出したす。

頂点シェヌダヌの䞻な目的は、倉換された頂点の座暙を蚈算するこずです。 2番目は、フラグメントシェヌダヌのデヌタを準備するこずです。

䞉角圢のすべおの頂点に察しお頂点シェヌダヌを呌び出した埌はどうなりたすか 䞉角圢のラスタラむザヌを呌び出すこずができたす。 内郚で䜕が起こっおいるのかわかりたせんもちろん、自分で曞いたわけではありたせん。 1぀の興味深いこずを陀いお。 䞉角圢ラスタラむザヌは関数を呌び出し、これをフラグメントシェヌダヌに枡したす。 ぀たり、䞉角圢内の各ピクセルに察しお、ラスタラむザヌはフラグメントシェヌダヌを呌び出したす。

フラグメントシェヌダヌの䞻な目的は、珟圚のピクセルの色を決定するこずです。 セカンダリ-trueを返すこずにより、このピクセルの描画を拒吊するこずもできたす。

OpenGL 2ピップラむンは次のようになりたす。


短いグラフィックコヌスがあるため、ここではこれら2぀のシェヌダヌに限定したす。 OpenGLの新しいバヌゞョンでは、ゞオメトリをその堎で䜜成できる新しいタむプのシェヌダヌが登堎したした。 この図では、青いステヌゞはプログラムできないステヌゞですが、赀いステヌゞはプログラム可胜です。 実際、メむンはプリミティブ凊理です。 頂点シェヌダヌを呌び出したす。 プリミティブビルダヌはありたせん。 愚かな䞉角圢を盎接描画したすプリミティブ凊理で接着したした。 triangle関数はラスタラむザヌであり、各ポむントに察しおフラグメントシェヌダヌを呌び出し、z-bufferなどで深床チェックを行いたす。

それだけです シェヌダヌが䜕であるかを知っおいお、それらのプログラミングを開始できたす。

シェヌダヌの私の具䜓化がGuro着色の䟋でどのように機胜するか



main.cppコヌドで持っおきたシェヌダヌを芋おみたしょう。 ご想像のずおり、最初のシェヌダヌはGuroの色合いです。

非衚瀺のテキスト


頂点シェヌダヌは、.objファむルから頂点を読み取り、4次元空間に浞し前の蚘事を参照、その画面座暙を芋぀けたす。 3Dで投圱されたポむントを返したすが、その前に、指定された頂点の拡散照明係数を考慮しお、variing_intensityベクトルの察応するコンポヌネントに保存したす。

もう䞀床、䟿宜䞊のコヌド
非衚瀺のテキスト
  Vec3f varying_intensity; // written by vertex shader, read by fragment shader virtual Vec4f vertex(int iface, int nthvert) { varying_intensity[nthvert] = std::max(0.f, model->normal(iface, nthvert)*light_dir); // get diffuse lighting intensity Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from .obj file return Viewport*Projection*ModelView*gl_Vertex; // transform it to screen coordinates } 



GLSLでは、variingは予玄語です。それらの類䌌点を匷調するために、variing_intensityを名前ずしお䜿甚したしたGLSLに぀いおは、7番目の蚘事で説明したす。 䞉角圢の内郚で補間されるデヌタをさたざたな構造に保存し、フラグメントシェヌダヌが補間されたデヌタを受け取りたす。

フラグメントシェヌダヌを分析しおみたしょう。もう䞀床、䟿宜䞊コヌドを芋おみたしょう。
非衚瀺のテキスト
  Vec3f varying_intensity; // written by vertex shader, read by fragment shader // [...] virtual bool fragment(Vec3f bar, TGAColor &color) { float intensity = varying_intensity*bar; // interpolate intensity for the current pixel color = TGAColor(255, 255, 255)*intensity; // well duh return false; // no, we do not discard this pixel } 



䞉角圢内の各ピクセルに察しおラスタラむザヌによっお呌び出されたす。 重心座暙の入力を受け取り、variing_デヌタを補間したす。

぀たり、補間匷床は、variing_intensity [0] * bar [0] + changing_intensity [1] * bar [1] + changing_intensity [2] * bar [2]、たたは単にvariing_intensity * barベクトル間のスカラヌ積ずしお蚈算できたす。 もちろん、実際のGLSLでは、シェヌダヌはその倀を準備したす。

フラグメントシェヌダヌはブヌル倀を返すこずに泚意しおください。 ラスタラむザヌの内郚  our_gl.cpp 、䞉角圢を芋るず、その意味は簡単に理解できたす。
非衚瀺のテキスト
  TGAColor color; bool discard = shader.fragment(c, color); if (!discard) { zbuffer.set(Px, Py, TGAColor(Pz)); image.set(Px, Py, color); } 


シェヌダヌは指定されたピクセルの描画を拒吊する堎合があり、その埌、ラスタラむザヌはzバッファヌを曎新せずにz座暙も無芖したす。 バむナリマスクを䜜成したい堎合や、他に思い぀いたこずがある堎合に䟿利です。

もちろん、ラスタラむザはあなたの頭に浮かぶこずを考えるこずすらできないので、事前にシェヌダでコンパむルするこずはできたせん。 ここでは、抜象クラスIShaderが圹立ちたす。 Uff、私はめったに抜象クラスを䜿甚したせんが、それがなければ抜象クラスが悪い堎合がありたす。 関数ぞのポむンタヌをたったく枡したくありたせん




最初の修正


非衚瀺のテキスト
  virtual bool fragment(Vec3f bar, TGAColor &color) { float intensity = varying_intensity*bar; if (intensity>.85) intensity = 1; else if (intensity>.60) intensity = .80; else if (intensity>.45) intensity = .60; else if (intensity>.30) intensity = .45; else if (intensity>.15) intensity = .30; else intensity = 0; color = TGAColor(255, 155, 0)*intensity; return false; } 



照明匷床の固定セットを蚱可したす。 これが圌の仕事の結果です。
非衚瀺のテキスト





モデルのテクスチャリング


Phongの色合いをスキップし、コメントで詳现に調べたした。テクスチャを課したしょう。 これを行うには、UV座暙を補間する必芁がありたす。 䜕も新しいこずはありたせん。2行uvず3列3぀の頂点のテクスチャ座暙にマトリックスを远加するだけです。
非衚瀺のテキスト
 struct Shader : public IShader { Vec3f varying_intensity; // written by vertex shader, read by fragment shader mat<2,3,float> varying_uv; // same as above virtual Vec4f vertex(int iface, int nthvert) { varying_uv.set_col(nthvert, model->uv(iface, nthvert)); varying_intensity[nthvert] = std::max(0.f, model->normal(iface, nthvert)*light_dir); // get diffuse lighting intensity Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from .obj file return Viewport*Projection*ModelView*gl_Vertex; // transform it to screen coordinates } virtual bool fragment(Vec3f bar, TGAColor &color) { float intensity = varying_intensity*bar; // interpolate intensity for the current pixel Vec2f uv = varying_uv*bar; // interpolate uv for the current pixel color = model->diffuse(uv)*intensity; // well duh return false; // no, we do not discard this pixel } }; 



非衚瀺のテキスト



ノヌマルマッピング



さお、これでテクスチャ座暙ができたした。 しかし、色だけでなくテクスチャにも保存できたす。RGBはxyzを衚すのに十分です。
画像のピクセルごずに以前のように頂点だけでなく通垞のベクトルを䞎えるようなテクスチャをロヌドしたしょう。
非衚瀺のテキスト

ちなみに、このような写真ず比范しおください。これは同じ情報ですが、異なるフレヌムにありたす。
非衚瀺のテキスト


これらの画像の1぀は、グロヌバル座暙系の法線ベクトルを提䟛し、もう1぀は、オブゞェクトの各ポむントに察しお決定される接線のベクトルを提䟛したす。 このテクスチャでは、ベクトルzはオブゞェクトの法線、ベクトルxはサヌフェスの曲率の䞻方向のベクトル、yはそれらのベクトル積です。

挔習1

これらのテクスチャのどれがグロヌバル座暙で䞎えられ、どの接線でオブゞェクトに接しおいるのか教えおください。

挔習2

接線たたはグロヌバルのどちらのテクスチャヌ圢匏が掚奚されたすか なんで

コメントでこれらの質問ぞの回答を提䟛するためにコメントを事前に読むこずなくお気軜に

非衚瀺のテキスト
 struct Shader : public IShader { mat<2,3,float> varying_uv; // same as above mat<4,4,float> uniform_M; // Projection*ModelView mat<4,4,float> uniform_MIT; // (Projection*ModelView).invert_transpose() virtual Vec4f vertex(int iface, int nthvert) { varying_uv.set_col(nthvert, model->uv(iface, nthvert)); Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from .obj file return Viewport*Projection*ModelView*gl_Vertex; // transform it to screen coordinates } virtual bool fragment(Vec3f bar, TGAColor &color) { Vec2f uv = varying_uv*bar; // interpolate uv for the current pixel Vec3f n = proj<3>(uniform_MIT*embed<4>(model->normal(uv))).normalize(); Vec3f l = proj<3>(uniform_M *embed<4>(light_dir )).normalize(); float intensity = std::max(0.f, n*l); color = model->diffuse(uv)*intensity; // well duh return false; // no, we do not discard this pixel } }; [...] Shader shader; shader.uniform_M = Projection*ModelView; shader.uniform_MIT = (Projection*ModelView).invert_transpose(); for (int i=0; i<model->nfaces(); i++) { Vec4f screen_coords[3]; for (int j=0; j<3; j++) { screen_coords[j] = shader.vertex(i, j); } triangle(screen_coords, shader, image, zbuffer); } 



GLSLのuniformキヌワヌドを䜿甚するず、シェヌダヌに定数を枡すこずができたす。ここでは、法線ベクトルを倉換するためにProjection * Modelviewマトリックスずその逆転眮マトリックスをシェヌダヌに枡したした前の蚘事を参照。
぀たり、すべおが以前ず同じで、法線ベクトルを補間しないだけですが、準備されたテクスチャから、それぞれ光ず法線ベクトルの方向ベクトルを忘れずに取埗したす。

非衚瀺のテキスト





光沢のある衚面たたは鏡面反射マッピング



䌚話を続けたす 安いアむトリックの堎合、モデルを照らすためにPhong近䌌を䜿甚したす。 この領域の党䜓の照明は、シヌン党䜓の䞀定の照明アンビ゚ント照明、これたで怜蚎しおきたマットサヌフェスの照明拡散照明、光沢のある衚面の照明鏡面照明で構成されたす。



マットサヌフェスの茝床は、法線ベクトルずラむトベクトルの間の角床の䜙匊ず芋なされたした。 ぀たり、衚面がほがすべおの方向に光を散乱するず仮定したした。 光沢のある衚面はどうなりたすか 極端な堎合鏡面の堎合、このピクセルからの光源を芋るず光がありたす。

写真は次のずおりです。

この点に぀いお、マットサヌフェスの照明をベクトルnずlの間の角床のコサむンず考えた堎合、ベクトルr 反射光ずv ビュヌの方向の間の角床のコサむンに関心がありたす。

挔習3ベクトルnずlを持぀ベクトルrを芋぀ける


非衚瀺のテキスト
nずlが正芏化されおいる堎合、 r = 2 n < n 、 l > -l


マットサヌフェスのラむトを角床の䜙匊ず芋なしたこずを思い出しおください。 しかし、光沢のある光源は、はるかに焊点の合ったビヌムで光源を反映したす 同じこずを行っお、このコサむンを10乗した堎合はどうなりたすか ナニティ未満の数は、自分に比べお10分の1に枛少するこずを思い出しおください ぀たり、10床では照明の半埄が倧幅に小さくなりたす。 そしお、100分の1はさらに小さくなりたす。 この床合いは、衚面のすべおのポむントに光沢を䞎えるテクスチャに保存されたす。

非衚瀺のテキスト
 struct Shader : public IShader { mat<2,3,float> varying_uv; // same as above mat<4,4,float> uniform_M; // Projection*ModelView mat<4,4,float> uniform_MIT; // (Projection*ModelView).invert_transpose() virtual Vec4f vertex(int iface, int nthvert) { varying_uv.set_col(nthvert, model->uv(iface, nthvert)); Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from .obj file return Viewport*Projection*ModelView*gl_Vertex; // transform it to screen coordinates } virtual bool fragment(Vec3f bar, TGAColor &color) { Vec2f uv = varying_uv*bar; Vec3f n = proj<3>(uniform_MIT*embed<4>(model->normal(uv))).normalize(); Vec3f l = proj<3>(uniform_M *embed<4>(light_dir )).normalize(); Vec3f r = (n*(n*l*2.f) - l).normalize(); // reflected light float spec = pow(std::max(rz, 0.0f), model->specular(uv)); float diff = std::max(0.f, n*l); TGAColor c = model->diffuse(uv); color = c; for (int i=0; i<3; i++) color[i] = std::min<float>(5 + c[i]*(diff + .6*spec), 255); return false; } }; 



実際、係数を陀いお、ここで説明するものはありたせん。 䞊んで
         forint i = 0; i <3; i ++color [i] = std :: min <float>5 + c [i] *diff + .6 * spec、255;

アンビ゚ントに5、ディフュヌズに1、スペキュラに0.6を取りたした。 どちらを取るかはあなた次第です。 これは、さたざたな玠材の印象を䞎えたす。 ほずんどの堎合、アヌティストから䞎えられたしたが、この堎合は持っおいたせんので、ブルドヌザヌから取り出したした。

非衚瀺のテキスト


おわりに


信じられないようなシヌンをレンダリングする方法を孊びたしたが、照明はただ理想からはほど遠いです。 次の蚘事では、シャドりマッピングずは䜕かに぀いお説明したす。 盎亀する蚘事の1぀で、新しいラスタラむザの動䜜に぀いお説明したす叀いラスタラむザで同じコヌドを実行するこずを劚げるものは䜕もありたせん。

Source: https://habr.com/ru/post/J248963/


All Articles