فهميدن نحوه عملکرد texture map فوايد زيادي دارد . فرض کنيد مي خواهيد يک سفينه يا يک موشک يا هر جسمي را روي صفحه به حرکت درآوريد . تا قبل از اين درس براي انجام اين کار بايستي اين جسم را به کمک چند ضلعي ها و رنگ آميزي آنها رسم کنيم . با texture map شما مي توانيد يک تصوير واقعي از يک موشک را بگيريد و آنرا در صفحه حرکت دهيد . به نظر شما کدام روش بهتر است ؟ يک عکس يا يک شيء که با مثلث و مربع رسم شده است ؟ با texture map نه تنها تصوير نهايي بهتر خواهد شد ، بلکه برنامه شما سريع تر اجرا خواهد شد . ( اين تيکه رو قبول ندارم هر چند ...) يک موشک texture map شده يک مربع است که در صفحه حرکت مي کند . يک موشک که با چند ضلعي ها رسم شده باشد ، از صدها و يا هزاران چند ضلعي تشکيل شده است . يک مربعي که تنها texture map شده است پروسس کمتري مي گيرد و منابع کمتري از سيستم مصرف مي کند .
کار را با اضافه کردن شش خط جديد به ابتداي برنامه شروع مي کنيم . دو خط اول از دستورات OpenGLهستند که پايين تر مفصل توضيح خواهيم داد . سپس ما سه متغير جديد از نوع مميزدار تعريف کرده ايم که با اين متغيرها مکعب را حول محور x ها ، y ها و z ها خواهيم چرخاند . آخرين خط يعني texture: array [0..0] of GLuint وظيفه اختصاص فضا براي نگهداري يک الگو ( texture ) را دارد . اگر مي خواهيد الگوهاي بيشتري استفاده کنيد ، مقدار اعضاي اين آرايه را بيشتر کنيد .

 
   

procedure glGenTextures(n: GLsizei; var textures: GLuint); stdcall; external Opengl32;

// ( NEW )
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;// ( NEW )
var 
  h_RC: HGLRC; // Permanent Rendering Context
  h_DC: HDC;// Private GDI Device Context
  keys: array [0..255] of BOOL; // Array Used For The Keyboard Routine
   
  xrot: GLfloat;// X Rotation( NEW )
  yrot: GLfloat;// Y Rotation( NEW )
  zrot: GLfloat;// Z Rotation( NEW )
  
  texture: array [0..0] of GLuint; // Storage For One Texture ( NEW )

 

 

   
 

حالا بلافاضله تابع مربوط به بارگزاري و اجراي الگو را مي نويسيم . کار اين بخش از کد بارگزاري يک فايل تصويري (Bitmap) است . اگر فايل وجود نداشته باشد ، از برنامه خارج خواهيم شد . قبل از اينکه شروع به کد نويسي کنيم چند مورد بسيار مهم وجود دارد که در رابطه با فايلهاي تصويريي که قرار است به عنوان الگو بکار بروند بايد بدانيد . نخست اينکه طول و عرض آنها بايد تواني از 2 باشد . بايد حداقل 64 پيکسل داشته باشند و براي سازگاري بيشتر نبايد بيش از 256 رنگ داشته باشند . اگر تصوير شما داراي 64 ، 128 يا 256 پيکسل نيست ، با يک برنامه مخصوص اينکار ( مثل Photoshop ) اندازه آن را درست کنيد . راههاي مختلفي براي کنار گذاردن اين محدوديت وجود دارد ، ولي فعلاٌ با اندازه هاي استاندارد الگوها کار مي کنيم .
اولين کاري که انجام مي دهيم تعريف گيره فايل است ( handle ) . پس از اينکار مي توانيم فايل تصويري را به اين متفير وصل کنيم و فايل را بارگذاري کنيم .

 
   

procedure LoadGLTextures;// Load Bitmaps And Convert To Textures
var 
  texture1: TAUX_RGBImageRec;  

begin

 
  texture1 := auxDIBImageLoadA('Data/nehe.bmp'); // Load Texture
 if (not Assigned(texture1)) then  
    Halt(1); 

 

   
 

حالا وقتي ما تصوير را بارگزاري کرديم ، بايد يک الگو براي آن بسازيم . خط اول يعني glGenTextures(1, texture[0]) به OpenGL مي گويد که ما مي خواهيم يک نام براي الگوي ما بسازد . ( اگر تعداد بيشتري الگو را بارگزاري کرده ايد ، مقدار 1 را بيشتر کنيد ) . به ياد داشته باشيد که در ابتداي اين کد ما فضايي را براي يک الگو دخيره کرديم .
خط دوم يعني
glBindTexture(GL_TEXTURE_2D, texture[0]) به OpenGL مي گويد که الگوي نام گداري شده texture[0] را به الگوي هدف متصل کند . يک الگوي 2 بعدي شامل ارتفاع ( در محور y ها ) و عرض ( در محور x ها ) است . کار اصلي glBindTexture نسبت دادن نام به داده هاي الگو است . در اينجا ما به OpenGL مي گوييم که اين اطلاعات در حافظه قرار دارد و با کمک متغيري به نام texture[0] قابل دستيابي است .

 
   

  glGenTextures(1, texture[0]); // Create The Texture
  glBindTexture(GL_TEXTURE_2D, texture[0]);// Typical Texture Generation Using Data From The Bitmap

 

   
 

دو خط بعدي به OpenGLمي گويند که اگر تصوير بزرگتر از صفحه بود GL_TEXTURE_MAG_FILTER يا وقتي که کوچکتر بود GL_TEXTURE_MIN_FILTER از چه روشي براي فيلترينگ استفاده کند . من معمولاٌ از GL_LINEAR استفاده مي کنم . اينکار باعث مي شود تصوير بهتري بدست بيايد . و البته منابع بيشتري از cpu و کارت گرافيک شما مصرف مي کند . در مقابل GL_NEAREST منابع کمتري مصرف مي کند ولي مثلاً وقتي تصوير تغيير اندازه داده مي شود آنرا به صورت مربع مربع خواهيد ديد . البته شما مي توانيد ترکيبي از اين دو را بکار ببريد و نتيجه را ببينيد .

 
   

  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering

 

   
 

در خط بعدي به OpenGLمي گوييم که يک الگوي واقعي را براي ما بسازد . اين خط به OpenGL مي گويد که اين الگو ، يک الگوي دو بعدي خواهد بودي . عدد صفر نمايانگر مشخصات تصوير است که معمولا صفر است . عدد سه معرف تعداد اجزاي تشکيل دهنده تصوير خواهد بود . چون تصوير ما از سه رنگ فرمز ، سبز و آبي تشکيل شده است ، پس تصوير ما از سه جزء تشکيل شده است . texture1^.sizeX عرض تصوير است . اگر عرض تصوير را مي دانيد عدد آنرا در اين قسمت قرار دهيد ولي آسانترين راه اين است که از اين روش استفاده کنيد تا اگر عرض ( و يا ارتفاع ) تصوير تغيير کرد نيازي به تغيير کد برنامه تان نباشد . texture1^.sizeY هم که ارتفاع تصوير است . صفر اندازه قاب يا حاشيه تصوير است . که معمولا صفر باقي مي ماند . GL_RGB به OpenGL مي گويد که اطلاعات تصوير کا از قرمز ، سبز و آبي به ترتيبي که نام برده شدند ، تشکيل شده است . GL_UNSIGNED_BYTE هم بدين معني است که اطلاعات تشکيل دهنده فايل تصويري ما از نوع بايت و بدون علامت هستند و در آخر هم texture1^.data به OpenGL مي گويد که اطلاعات الگو را از کجا دريافت کند . که در اينجا به محلي درون texture1 اشاره مي کند که اطلاعات تصوير بارگزاري شده در آن محل ذخيره شده بود .

 
   

  glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1^.sizeX, texture1^.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1^.data); // Generate The Texture
end; 

 

   
 

حالا بايد چند خطي را هم به روال InitGL اضافه کنيم . من تمام اين روال را اينجا دوباره مي آورم تا به راحتي خطوطي که اضافه شده اند و مکان اضافه شدن آنها قابل تشخيص باشد . اولين خط روال LoadGLTextures را صدا مي زند تا الگو بارگزاري و ساخته شود . سپس الگوي دوبعدي فعال مي شود . اگر اين کار را نکنيد اجسام شما سفيد نمايش داده خواهند شد که مطمئناً نمايش خوبي نخواهد بود .

 
   

procedure InitGL(Width: GLsizei; Height: GLsizei); // All Setup For OpenGL Goes Here
var  
  fWidth, fHeight: GLfloat;  
begin  
  LoadGLTextures(); // Load The Texture(s)
 glEnable(GL_TEXTURE_2D);// Enable Texture Mapping
  
  glClearColor(0.0, 0.0, 1.0, 0.0); // Clear The Background Color To Blue
  glClearDepth(1.0); // Enables Clearing Of The Depth Buffer
  glDepthFunc(GL_LESS); // The Type Of Depth Test To Do
  glEnable(GL_DEPTH_TEST); // Enables Depth Testing
  glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
   
  glMatrixMode(GL_PROJECTION); 
  glLoadIdentity();// Reset The Projection Matrix
   
  fWidth := Width;  
  fHeight := Height;  
  gluPerspective(45.0,fWidth/fHeight,0.1,100.0);// Calculate The Aspect Ratio Of The Window
  
  glMatrixMode(GL_MODELVIEW); 
end; 

 

   
 

حالا ما مکعب را با الگو رسم مي کنيم . شما مي توانيد کد روال DrawGLScene را با کد زير تعويض کنيد يا مي توانيد کدهاي اضافه شده را به متن اصلي مه در درس يک نوشته بوديم اضافه کنيد . اين قسمت را به طور کامل شرح خواهيم داد تا فهميدن منظور آن راحتتر شود .
دو خط اول که در کد درس اول نيز وجود داشتند صفحه را با رنگي که در InitGL معين کرده ايم پاک مي کنند . در اينجا اين رنگ سياه خواهد بود . بافر عمق نيز خالي مي شود . سپس منظره نمايش نيز با دستور glLoadIdentity به حالت اوليه برگردانده مي شود .

 
   

procedure DrawGLScene(); 
begin 
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);// Clear The Screen And The Depth Buffer
  glLoadIdentity();// Reset The View
  glTranslatef(0.0,0.0,-5.0);  

 

   
 

در سه خط بعدي مکعب در محورهاي x و y و z چرخانده مي شود . مقدار اين چرخش به مقدار متغيرهاي xrot و yrot و zrot بستگي دارد .

 
   

  glRotatef(xrot,1.0,0.0,0.0);  
  glRotatef(yrot,0.0,1.0,0.0);  
  glRotatef(zrot,0.0,0.0,1.0);  

 

   
 

در خط بعدي ما الگوي مورد نظر خود را انتخاب مي کنيم . اگر تعداد الگوهاي بيشتري را داشتيد مي توانيد به جاي عدد 0 از عدد مورد نظر خود براي استفاده از آن الگو استفاده کنيد . اگر بخواهيد از الگوي ديگري استفاده کنيد بايد آنرا انتخاب کنيد . چيزي که بايد به خاطر داشته باشيد آن است که شما نمي توانيد الگوي خود را بين دستورات glBegin و glEnd عوض کنيد . يعني اين کار بايد قبل ازglBegin  و يا بعد از دستور glEnd انجام شود .

 
   

glBindTexture(GL_TEXTURE_2D, texture[0]);// Select Our Texture

 

   
 

براي اينکه يک مربع به طور صحيح با يک الگو پر شود بايد به خاطر داشته باشيد که بالا سمت راست الگو با قسمت بالا سمت راست يک مربع تنظيم شده سپس بالا سمت چپ الگو با بالا سمت چپ مربع ، سپس پايين سمت راست و در نهايت پايين سمت چپ آنها با هم منطبق مي شوند . اگر اندازه الگو با مربع يکي نباشد ممکن است از کناره يا از بالاي مربع بيرون بزند و يا مربع را به طور کامل پر نکند .
دستور glNormal3f را در درس بعد به طور مفصل بررسي خواهيم کرد . در اينجا همينقدر بدانيد که براي مشخص کردن روي يک چند ضلعي ( در مقابل پشت آن ) بکار مي رود و عمده استفاده آن در مبحث نور و نورپردازي هاست . دستور glTexCoord2f دو پارامتر دارد که مشخص مي کند کدام ناحيه از الگو بايد رسم شود ( رسم از کدام ناحيه الگو بايد شروع شود ) . مقدار 0.0 بدين معناست که بايد از سمت چپ الگو شروع شود . 0.5 به معناي شروع از وسط الگو و 1.0 به معناي شروع از سمت راست الگو مي باشد . ( در حقيقت اين بدان معناست که جهت رسم الگو به سمت مرکز مربع چگونه باشد ؟ مثلاً وقتي ما نقطه بالا سمت راست مربع را رسم مي کنيم بايد هر دو پارامتر اين دستور يک باشد زيرا هم در راستاي محور x ها و هم در راستاي محور y ها رسم از انتهاي الگو بايد شروع شود . اين موضوع را حتما امتحان کنيد ) .
حالا که ما مي دانيم که بالا سمت چپ الگو با 0.0 در x و با 1.0 در y مشخص مي شود و در رسم مربع اين اعداد -1.0 براي x و 1.0 براي y است ، رسم را شروع مي کنيم . حالا تمام کاري که بايد بکنيم اين است که سه نقطه ديگر مربع را نيز با الگو منطبق کنيم .
سعي کنيم مقادير x و y را در glTexCoord2f عوض کنيد و مقدار 0.5 را برگزينيد . خواهيد ديد که الگو از ميانه رسم مي شود .

 
   

  glBegin(GL_QUADS);  
// Front Face 
    glNormal3f( 0.0, 0.0, 1.0); /// Normal Pointing Towards Viewer
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);// Bottom Left Of The Texture and Quad
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, 1.0);// Bottom Right Of The Texture and Quad
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, 1.0);// Top Right Of The Texture and Quad
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0);// Top Left Of The Texture and Quad
// Back Face 
    glNormal3f( 0.0, 0.0,-1.0);  
    glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);// Bottom Right Of The Texture and Quad
    glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);// Top Right Of The Texture and Quad
    glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, 1.0, -1.0);// Top Left Of The Texture and Quad
    glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);// Bottom Left Of The Texture and Quad
// Top Face  
    glNormal3f( 0.0, 1.0, 0.0);  
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);// Top Left Of The Texture and Quad
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 1.0);// Bottom Left Of The Texture and Quad
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, 1.0, 1.0);// Bottom Right Of The Texture and Quad
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, -1.0);// Top Right Of The Texture and Quad
// Bottom Face  
    glNormal3f( 0.0,-1.0, 0.0);  
    glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0);// Top Right Of The Texture and Quad
    glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, -1.0, -1.0);// Top Left Of The Texture and Quad
    glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, 1.0);// Bottom Left Of The Texture and Quad
    glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);// Bottom Right Of The Texture and Quad
// Right face  
    glNormal3f( 1.0, 0.0, 0.0);  
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);// Bottom Right Of The Texture and Quad
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, -1.0);// Top Right Of The Texture and Quad
    glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, 1.0, 1.0);// Top Left Of The Texture and Quad
    glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, 1.0);// Bottom Left Of The Texture and Quad
// Left Face  
    glNormal3f(-1.0, 0.0, 0.0);  
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);// Bottom Left Of The Texture and Quad
    glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);// Bottom Right Of The Texture and Quad
    glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0);// Top Right Of The Texture and Quad
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);// Top Left Of The Texture and Quad
  glEnd(); 

 

   
 

حالا ما مقادير xrot و yrot و zrot را تغيير مي دهيم تا مکعب ما بچرخد . سعي کنيد مقادير را کم و زياد کنيد تا سرعت چرخش مکعب در آن راستا کم و يا زياد شود و يا مقادير را تغيير علامت دهيد تا تغيير جهت چرخش مکعب را ببينيد .

 
   

  xrot := xrot + 0.3; // X Axis Rotation
  yrot := yrot + 0.2; // Y Axis Rotation
  zrot := zrot + 0.4; // Z Axis Rotation
end; 

 

   
 

حالا شما درک بهتري از texture map داريد . شما با texture map مي توانيد سطح هر مربعي را با هر تصويري که بخواهيد بپوشانيد . وقتي احساس کرديد که با مفهوم texture map دوبعدي مشگلي نداريد سعي کنيد که هر وجه از شش وجه مکعب را با يک تصوير بپوشانيد .
درک مفهوم texture map بسيار آسان است و تنها قسمت مشگل آن فهميدن مختصات الگوي شماست . اگر نتوانستيد دليل ايرادي را بفهميد و يا مشگل خاصي داشتيد با من در ميان بگذاريد


 
   

  

دريافت ليست دلفي نوشته شده توسط Brad Choate

 

درس هفتم ->

<- درس پنجم