# مباحث متفرقه برنامه نویسی > طراحی و ساخت بازی‌های کامپیوتری > آموزش: آموزش (OpenGL 3.2 – OpenGL 4.2)  و زبان شیدر GLSL

## amin1softco

خوب ایام تابستان 92 را دوست داشتم اگر بتونم روی اپن جی ال کار کنم پس تصمیم گرفتم در این بخش یک تاپیک جدید با هدف آموزش اصولی اپن جی ال نسل جدید ایجاد کنم این تاپیک بدرد افراد حرفه ایی نمی خوره این بیشتر برا مبتدیان است و کسایی مثل من که بصورت تفریحی دوست دارند با این API کار کنند.

----------


## agep110

*شروع کن*  :لبخند گشاده!:

----------


## amin1softco

در نمودار زیر ما مسیر تولید تصویر نهایی در OpenGL نسخه 3.2  به بالا را مشاهده می کنیم در قسمت بعدی ما هر یک از این قسمت ها را بصورت مبسوط تشریح می کنیم اعدادی که در پرانتز روی شکل مشخص شده نسخه ایی را مشخص می کنه که می تونه از اون مسیر استفاده کنه .
pipeline1.png
منبع ترجمه ایی از + است.

خوب من به نظرم همه دوست دارند قبل از هر چیزی با نحوه ساخت اولین پروژه در IDE مثل ویژوال استادیو آشنا بشوند ولی قبل از اینکه ما یک کتابخانه را برای کار خودمون در سیستم عامل ویندوز انتخاب کنیم باید بگم که OpenGL در هر سیستم عامل و هر زبانی قابل استفاده است و گروه خرُنوس  (Khronos Group)  توسعه دهنده این بسته گرافیکیشامل شرکت های معروفی مثل گوگل ، اپل ، انویدیا است. 
به هر حال زبانی که ما استفاده می کنیم در این قسمت آموزشی سی++ و سیستم عامل ویندوز سون است و چون ما در حال استفاده از نسخه 3.2 جی ال به بالا هستیم باید این نکته را در نظر داشته باشید که کارت گرافیک شما این نسخه را پشتیبانی کند .

از آنجایی که OpenGL یک بسته گرافیکی است و ما به کتابخانه های دیگری برای رفع نیاز های خود محتاجیم باید شما با این سه کتابخانه آشنا شوید و آنها را در پروژه های خودتون بکار ببرید : 


*FreeGLUT*
برای ساخت پنجره ها و کانتکس ما نیاز به یک کتابخانه داریم که بتونیم برنامه خودمون را بصورت کراس پلاتفرم منتشر کنیم برای همین ما از FreeGLUT استفاده می کنیم که معمولاً اگر هدف خروجی تحت یک پلاتفرم خاص مثل ویندوز باشه از توابع win32 برای ساخت پنجره استفاده می کنند ولی ما قصد داریم تحت گنو لینوکس هم بتونیم خروجی های خودمون را بگیریم برای همین شما باید به آدرس زیر بروید و نسخه2.6.0  یا بالا تر از این کتابخانه را تهیه کنید تا OpenGL4 را نیز پشتیبانی کند . http://freeglut.sourceforge.net/
FreeGLUT تحت لیسانس  X-Consortium منتشر شده به این معنی که شما می تونید از اون در هر برنامه ایی استفاده کنید حتی اگر اون برنامه فروشی باشد .


*GLEW*
بارگزاری بی دردسر اکستنشن های وابسته به پلاتفرم !  GLEW یک مکانیزم موثر زمان-اجرا برای مشخص کردن اکستنشن های پشتیبانی شده در پلاتفرم هدف است   , بنا براین کتابخانه بعدی هم مشخص شد (OpenGL Extension Wrangler) جمع اور اکستنشن های آپن جی ال :دی , شما می توانید یک کپی از این کتابخانه را در آدرس http://glew.sf.net/ به رایگان تهیه کنید که متن باز و تحت لیسانس several unrestricted licenses است که به شما اجازه استفاده در یک کد,شبیه freeglut می دهد مطمئن شوید نسخه 1.5.4 به بالا را برای پشتیبانی از OpenGL4.0 تهیه کنید .

*DevIL*
آخرین کتابخانه ایی که ما به آن نیاز داریم برای لود کردن تصاویر در برنامه شما که در بخش بافت ها زیاد کاربرد دارد دویل مخف (کتاخانه عکس توسعه دهنده ها)(Developer’s Image Library) است شما می توانید آخرین نسخه http://openil.sf.net/ را از این آدرس دانلود کنید, هر چند دویل از لیسانس LGPL (GNU Lesser General Public License) استفاده می کند به این معنا که شما تا زمانی می تونید از سورس این بسته استفاده کنید که برنامه خود را تحت لیسانس  LGPL یا GPL باز نشر کنید.

خوب برای شروع ما کانفیگ ویژوال 2010 در ویندوز سون را اینجا قرار می دهیم برای ویژوال 2012 هم به همین شکل است :
*مرحله اول :* تهیه کتابخانه هایی که در بالا اشاره شد 
1- لینک مستقیم freeglut اینجا
 2- لینک مستقیم glew اینجا 

step-1.png
*مرحله دوم :* کتابخانه ها را از حالت فشرده خارج کنید 
برای ویندوز های 32 بیتی به آدرس زیر بروید: (متن فوق را در Run یا پنجره اکسپلورر وارد کنید)
%PROGRAMFILES%\Microsoft SDKs\Windows\
برای ویندوز های 64 بیتی به آدرس زیر بروید :
%PROGRAMFILES(X86)%\Microsoft SDKs\Windows\
سپس فولدری که بیشترین ورژن را مشخص می کند را باز کنید مثلاً v7.0A و محتویات پوشه FreeGlut را اینجا پیست کنید باید سه شاخه lib و include و bin به اینجا پیست شوند همچنین وارد پوشه glew شده و به همین ترتیب محتویاتش را به اینجا منتقل کنید .
step-2.jpg
*مرحله سوم* : ساخت پروژه 
برای ساخت پروژه ابتدا ویژوال استادیو را اجرا کنید و سپس مسیر زیر را دنبال کنید 
File -> New -> Project… 
سپس Win32 Console Application را انتخاب کنید 
تیک *Empty Project* را بردارید و سپس روی finish کلیک کنید .

step-4.jpg


*مرحله چهارم* : 
.
.

----------


## agep110

سلام.  :لبخند گشاده!: خسته نباشی دوست عزیز.
به نظرم اگر هر مطلب رو تو یه پست جدا بزاری بهتره. :چشمک:

----------


## amin1softco

چشم *agep110* جون .
step-5.png

step-7.png



*مرحله چهارم :* اضافه کردن فایل سورس 
الان ما یک پروژه خالی داریم و نیاز است که به آن سورس را اضافه کنیم برای اینجا روی پروژه راست کلیک می کنیم و *add* و سپس *Add* *New Item…*
step-8.png
سپس نوع .cpp را انتخاب می کنیم و یک اسم به آن اختصای می دهیم مثل main.cpp
step-9.png
در اینجا شما باید یک سورس برای تست در این فایل وارد کنید می توانید از سورس ضمیمه استفاده کنید و محتویات آنرا در اینجا پیست کنید .

*مرحله پنجم* : تنظیمات پروژه (کامپایلر)
برای وارد کردن تنظیمات باید مسیر زیر را دنیال کنید رو پروژه کلیک راست کرده و *Properties* را انتخاب می کنیم .
step-11.png

----------


## amin1softco

*توجه* : مراقب باشید که این پیکر بندی روی تمام پروژه اعمال می شود از قسمت *Configuration*  گزینه *All Configurations* را انتخاب نمایید.
step-12.png
*مرحله 6* : تنظیمات لینکر 
وارد قسمت *Linker* شده و سپس *Input * را انتخاب کرده و در ابتدا فرم روی *Additional Dependencies* روی *Edit* کلیک کنید و *glew32.lib*, *freeglut.lib*  را مثل شکل زیر وارد کنید .
step-17.png

*مرحله 7 :* کامپایل و اجرای پروژه 
از منو build منو  *Build Solution * را انتخاب کنید اگر همه ی کار ها را به درستی انجام داده باشید باید بدون اخطار برنامه اجرا شود 
step-19.png
نمونه برنامه خالی :

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <GL/glew.h>
#include <GL/freeglut.h>
#define WINDOW_TITLE_PREFIX "Chapter 1"

int CurrentWidth = 800,
    CurrentHeight = 600,
    WindowHandle = 0;

void Initialize(int, char*[]);
void InitWindow(int, char*[]);
void ResizeFunction(int, int);
void RenderFunction(void);

int main(int argc, char* argv[])
{
    Initialize(argc, argv);

    glutMainLoop();
    
    exit(EXIT_SUCCESS);
}

void Initialize(int argc, char* argv[])
{
    InitWindow(argc, argv);
    
    fprintf(
        stdout,
        "INFO: OpenGL Version: %s\n",
        glGetString(GL_VERSION)
    );

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}

void InitWindow(int argc, char* argv[])
{
    glutInit(&argc, argv);
    
    glutInitContextVersion(4, 0);
    glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
    glutInitContextProfile(GLUT_CORE_PROFILE);

    glutSetOption(
        GLUT_ACTION_ON_WINDOW_CLOSE,
        GLUT_ACTION_GLUTMAINLOOP_RETURNS
    );
    
    glutInitWindowSize(CurrentWidth, CurrentHeight);

    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);

    WindowHandle = glutCreateWindow(WINDOW_TITLE_PREFIX);

    if(WindowHandle < 1) {
        fprintf(
            stderr,
            "ERROR: Could not create a new rendering window.\n"
        );
        exit(EXIT_FAILURE);
    }

    glutReshapeFunc(ResizeFunction);
    glutDisplayFunc(RenderFunction);
}

void ResizeFunction(int Width, int Height)
{
    CurrentWidth = Width;
    CurrentHeight = Height;
    glViewport(0, 0, CurrentWidth, CurrentHeight);
}

void RenderFunction(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glutSwapBuffers();
    glutPostRedisplay();
}


step-22.jpg
برای اینکه برنامه شما پس از انتقال به سیستم ها دیگر هم به خوبی اجرا شود شما باید دو فایل *freeglut.dll*  و *glew32.dll* را از پوشه bin به محل دیباگ یا ریلیز پروژه خود منتقل کنید . مثل شکل زیر .
step-21.jpg

برای دانلود یک پروژه کامل در وِیژوال استادیو 2010 از این لینک استفاده کنید .

----------


## amin1softco

یک سایه زن رأس روی رئوس منتخب عمل میکند یک رأس در یک زمان . سایه زن اطلاعاتی در مورد رئوس دیگر که یک شکل اولیه گرافیکی را ایجاد میک ند ندارد و نمی داند به چه شکل اولیه تعلق دارد . برای هر رأس ورودی . خروجی این شیدر یک رأس تنهاست . 
هر ورتکس دارای یک مجموعه از خصوصیات ورودی که توسط کاربر تعریف شده است می باشد . , برای مثال , موقعیت , بردار نرمال و موقعیت های بافت . سایه زن های رأس همچنین دارای دسترسی به متغیر های uniform هستند که بصورت متغیر های کلی فقط-خواندنی برای تمام رئوس در فرخوانی ترسیم است . 
در کنار متغیر های تعریف شده توسط کاربر , GLSL  یک مجموعه از خصوصیات را برای هر رأس  تعریف می کند:

in  int   gl_VertexID;
in  int   gl_InstanceID;

gl_VertexID به فهرست(شاخص) رئوس  در آرایه خصوصیات بر می گردد
  زمانی که از یک نمونه سایه زن استفاده می کنیم شیدر n بار برای هر رأس ورودی اجرا می شود که n در دستور glDraw* اپن جی ال مشخص می شود . متغیر gl_InstanceID فهرست از نمونه را مشخص می کند . زمانیکه از نمونه ها استفاده نشود این متغیر همیشه برابر صفر است . 
سایه زن رأس خصوصیات ورودی و رئوس خروجی  را دریافت می کند و خصوصیات هر یک را محاسبه میک ند . پس خصوصیات خروجی حقیقی برای نوشتن در سایه زن رأس اماده هستند :

out gl_PerVertex {
    vec4  gl_Position;
    float gl_PointSize;
    float gl_ClipDistance[];
};

نوشتن در هریک از اینها اختیاری است هر چند تعدادی از مراحل توابع ثابت بعد از سایه زن رأس به استثناء  gl_Position قابل نوشتن است . تا زمانی که این متغیر ها قصد ذخیره مختصات همگن از موقعیت رئوس خروجی است . 
بعلاوه , سایه زن می تواند خروجی تعریف شده توسط کاربر برای خصوصیات هر رأس داشته باشد .
این یک مثال ساده که نشان دهنده این خصوصیات است :

#version 410
 
layout (std140) uniform Matrices {
    mat4 projModelViewMatrix;
    mat3 normalMatrix;
};
 
in vec3 position;
in vec3 normal;
in vec2 texCoord;
 
out VertexData {
    vec2 texCoord;
    vec3 normal;
} VertexOut;
 
void main()
{
    VertexOut.texCoord = texCoord;
    VertexOut.normal = normalize(normalMatrix * normal);   
    gl_Position = projModelViewMatrix * vec4(position, 1.0);
}

در سایه زن رأس بالا سه خصوصیات تعریف ده توسط کاربر را یم گیرد برای هر رأس position, normal, و texCoord . که  یک بلوک uniform به نام Matrices که شامل دو ماتریس عمومی برای رأس و تبدیلات نرمال است دریافت می کند .
خروجی ها برای سایه زن رأس هر دو خصوصیات تعریف شده توسط کاربر هستند texCoordOut و normal و خصوصیت حقیقی gl_Position . نکته : متغیرهای خروجی در یک بلوک نامی محصور هستند .
خروجی ها در تابع main محاسبه می شود . هر نوع از شیدر باید دارای تابع main باشد و می تواند توابع کمکی تعریف کند . در راهی شبیه برنامه C

نکات بهبود عملکرد 
عملکرد داناست ! یک کش رأس که ذخیره می کند خروجی ها را برای آخرین n رئوس محاسبه شده قبل از اینکه راس جدید پردازش شود .  فهرست در برابر کش راس فهرست ها  چک شده است اگر فهرست در کش راس باشد بترتیب داده های پردازش شده قبلی باقی می مانند در مسیر تولید تصویر نهایی بدون پردازش بیشتری .

مزیت کش کردن راس , می تواند عملکرد را بهبود بخشد زمانیکه از فهرست استفاده می شود . چه به صراحت چه به طور ضمنی وقتی در یک مثلث نوارها و پره ها برای مثال در یک مثلث نواری ( triangle strip) , حداقل یک راس در هر مثلث جدید پردازش خواهد شد . زمانیکه استفاده می کنیم از فهرست با مثلث های منظم . این بهبود به این راحتی ها محاسبه نمی شود و فهرست ها شاید نیاز به تشخصی داده های رأس برای بهبود بازدید کش رأس داشته باشند.

زمانی که داده ای رأس یک نوار نیست , می تواند تبدیل به یک نوار یا مجموعه نوار بشود .  NVTriStrip یک ابزار از شرکت نویدیا که این عمل را انجام می دهد این یک آرایه از فهرست ها و سعی در ساخت نواری تا بیشتری حد ممکن بزرگ میک ند راهکارهای دیگر , Tom Forsyth’s algorithm (بر پایه یک کش از رئوس بر پایه آخرین استفاده شده نزدیک (LRU) سیاست جابجایی ) تشخیص داده های فهرست برای افزایش برخورد ها مد GL_TRIANGLES را حفظ کنید . Tootle, ابزار بهبود ترتیب مثلث ها یک ابزار از AMD است برای بهبود مدل ها  برای کاهش پیکسل های بیمحل افزایش تبدیل - بعدی کش و راس واکشی اولیه برخورد این بعد ها بهبود قابل دسترسی است بوسیله تشخیص داده های راس به خودی خود بنابراین رئوس این یک مثلث بسته هستند به یکدیگر روی حافظه . Adrian Stone یک مقاله نوشته که چندین الگوریتم بحث شده و تست شده Ignacio Castaño  تمرکز کرده روی نقاط روی مقاله خودش

----------


## amin1softco

قسمت اسمبل اولیه رئوس پردازش شده را به عنوان ورودی از سایه زن رأس  در یافت می کند, و اطلاعات اتصالات رئوس را از برنامه به گونه ایی که توسط توابع اپن جی ال شبیه glDraw* مشخص شده است . 
حالت اتصال رأس  چگونگی اتصال رئوس برای ساخت شکل اولیه می باشد (طرح)اولیه می تواند نقاط , خطوط , مثلث ها یا وصله ها باشد . بعلاوه , اطلاعات مجاورت نیز ممکن است وجود داشته باشد .برای مثال برنامه می تواند همچنین فراهم کند رئوسی که مجاورت ها را ایجاد میک ند . این اطلاعات فقط برای استفاده در شیدر هندسی است واگر  چنین شیدری فعال نباشد , اطلاعات مجاورت نادیده گرفته می شود.
خروجی اسمبل اولیه ,(طرحهای) اولیه و وصله هاست . برای مثال , اگر ورودی یک دنباله از 6 رأس باشد و اطلاعات اتصال مشخص کند ما باید از مثلث استفاده کنیم خروجی دو مثلث است . برای نوارهای مثلث (triangle strips) و در نظر گرفتن 6 راس خروجی ممکن است 4 مثلث باشد .
وصله ه نوعی (طرح) اولیه هستند که بوسیله شیدر کنترل فرش سازی قبول می شود , در OpenGL نسخه 4 به بعد پشتیبانی می شود . تعداد از رئوس از وصله  مثل مورد (طرح) اولیه ثابت نیستند و می توانند بین 1 و یک ثابت وابسته به پیاده سازی GL_MAX_PATCH_VERTICES رئوس برای هر وصله متغیر باشند .
بطور گرافیکی , بر فرض مثال نوع اولیه GL_TRIANGLES بصورت زیر تشریح می شود :




جدول زیر ورودی های ممکن و اطلاعات اتصال و خروجی های تولید شده را به گونه ایی که شیدر از انها استفاده می کند نمایش می دهد 


ogl.JPG
در زیر مثال هایی از نتیجه حاصل از نقاط , خطوط و مثلث ها است . موضوع (طرحهای) اولیه با اطلاعات اتصال , خطوط کامل شکل اولیه اصلی است و خطوط تیره اتصال رئوس مجاور است توجه کنید که ترتیب در گرافیک ها , همه (طرح های ) اولیه پاد ساعتگرد است بطور هوشمند. 

تفسیر هندسی از یک وصله بصورت خطی نیست بنابراین در اینجا نمایش داده نشده قسمت فرش سازی را برای اطلاعات بیشتر در مورد وصله ها ببینید

----------


## amin1softco

مفروش سازی قسمتی در خط تولید تصویر نهایی است که وصله ها را به عنوان ورودی دریافت می کند و (طرح های ) اولیه جدید که می توانند از نوع نقاط , خطوط , یا مثلث ها باشند تولید می کند .
یک وصله یک آرایه از رئوس است که خصوصیات انها توسط سایه زن رأس محاسبه شده است . برای سایه زن رأس این یک کار معمولی است , دریافت رئوس وصله به عنوان ورودی و تولید رئوس جدید در خروجی . شیدر مفرش سازی آرایه ایی از  رئوس تبدیل شده را دریافت کرده و غیره , وصله و معمولاً به زیر قسمت هایی که (طرح) اولیه ساده تر تبدیل می کند .
بر خلاف نوع های (طرح) اولیه اپن جی ال , وصله ها دارای تعداد رئوسی هستند که توسط کاربر مشخص شده . تابع glPatchParameteri برای مشخص کردن این مقدار استفاده می شود . که ثابت باقی می ماند در فراخوانی ترسیم به صورت زیر : 
glPatchParameteri( GL_PATCH_VERTICES, verticesPerPatch );
زمانیکه verticesPerPatch باید یک عدد صحیح در بازه  [1, GL_MAX_PATCH_VERTICES] باشد این ثابت می تواند با glGetIntegerv. بدست بیاید.
مسیر تولید شیدر مفروش سازی دارای سه زیر قسمت است : کنترل مفروش سازی , تولید (طرح) اولیه , و سنجش مفروش سازی . اولین و آخرین قسمت قابل برنامه نویسی است . برای مثال ما می توانیم شیدری هایی بنویسیم برای انها در حالیکه قسمت دوم ثابت است .

اولین قسمت , کنترل سایه زن مفروش سازی , دریافت می کند یک آرایه با رئوس ورودی وصله , و محاسبه میک ند خصوصیات را برای هریک از رئوس که وصله خروجی را ایجاد میک ند . همچنین در یک آرایه ذخیره می شوند . این سایه زن  ها همچنین مسئولیت  نوشتن خصوصیات برای هر -وصله را عهده دار هستند که توسط زیر قسمت  درجه از وصله تعریف می شود . 

اینجا دو نوع وصله موجود است : مثلث و مکعبی . نکته , هرچند , آنها تعداد رئوس در یک وصله به نوع انها بستگی ندارد  برای نمونه این ممکن است که یک وصله مکعب بوسیله یک رأس مجزا تعریف شود یا 16 رأس برای تعریف یک مسیر بیضوی .
زیر قسمت ها بوسیله یک سطح مفروش سازی کنترل می شوند این ها می تواند از 0 تا GL_MAX_TESS_GEN_LEVE مشخص شود . یک مقدار که می توان بوسیله glGetIntegerv دریافت کرد . این مقدار معمولاً 64 است .
سطوح مفروش سازی توسط زیر قسمت های هر طرف از وصله کنترل می شوند . و همچنین درونی . از این جهت ما برای یک مکعب 4 مفروش ساز بیرونی داریم و برای یک وصله مثلثی 3 تا . اینها برای یک مکعب  دو  سطح مفروش ساز داخلی نیاز است . 
نکته  سایه زن ممکن است موقعیت سطوح مفروش سازی را نادیده بگیرد . و از سطوح پیش تعریف شده استفاده کند . این مقادیر پیش فرض می تواند توسط تابع glGetIntegerv مورد پرس و جو قرار گیرد .

برنامه می تواند این مقادیر ار به صورت زیر مشخص و تغییر دهد :
glPatchParameteri( GL_PATCH_DEFAULT_OUTER_LEVEL, myDefaultOuterLevel );
glPatchParameteri( GL_PATCH_DEFAULT_INNER_LEVEL, myDefaultInnerLevel );

بعد از اینکه رئوس محاسبه شدند و سطوح مفروش سازی مشخص شد ما آماده اییم که به سطح 2 برویم . تولید (طرح) اولیه , این قسمت تابع ثابت است fixed function و مسئولیت تولید واقعی رئوس جدید داخل وصله است . نکته هرچند در این قسمت ما دانشی نسبت به وصله مان نداریم . به جای آن روی وصلخ قالب کار میک نیم با مختصات زیر : 

سطوح مفروش سازی تحت تأثیر  ناحیه های مشخص یا اطراف به صورتی که در شکل بالا مشخص شده تعریف شده است

----------


## amin1softco

شیدری هندسی یکی از قسمت های مشخصه اپن جی ال 3.2 است .
این قسمت اختیاری است , در اجرا , یک سایه زن هندسی (طرح) اولیه اسمبل شده در قسمت قبلی را به عنوان ورودی دریافت می کند یک سایه زن هندسی نوار ها , پره ها , یا حلقه ها را دریافت نمی کند . سایه زن هندسی (طرح) اولیه اسمبل شده دریافت می کند بنابراین اگر دستور ترسیم یک مثلث در حالت نواری را مشخص کند سایه زن هندسی در واقع مثلث ها را دریافت می کند .

سایه زن هندسی بر خلاف سایه زن رأس , دانش کامل نسبت به (طرح) اولیه که روی ان کار می کند دارد . برای هر ورودی اولیه , سایه زن هندسی دارای دسترسی به تمام رئوس ایجاد کننده (طرح) اولیه دارد از جمله اطلاعات مجاورت اگر مشخص شده باشد. 
ورودی (طرح) اولیه های فعال در سایه زن هندسی:

    points (1)
    lines (2)
    lines_adjacency (4)
    triangles (3)
    triangles_adjacency (6)
در پرانتز ها , تعداد رئوس برای هر طرح اولیه نشان داده شده است .

نکته ورودی های سایه زن رأس باید با دستور عمل ترسیم OpenGL همانطور که در قسمت اسمبل (طرح) اولیه آمد تطبیق داشته باشد . زمانیکه (طرح) اولیه با اطلاعات مجاورت دریافت می شود lines_adjacency یا triangles_adjacency , ترتیب رئوس در شکل زیر نمایش داده شده است :

خروجی هایی که برای (طرح) اولیه فعال هستند عبارت است از :

    points
    line_strip
    triangle_strip
تطبیق ورودی ها و خروجی ها لازم نیست . برای مثال , یک سایه زن رأس می تواند triangles دریافت و در خروجی points یا a line_strip داشته باشد.

خروجی یک سایه زن رأس می تواند صفر یا (طرح های ) اولیه بیشتر باشد . برای مثال , اگر خروجی ها مثلث نواری است , یک سای زن هندسی می تواند سه نوار یا دو مثلث برای هریک باشد . ورودی (طرح) اولیه همیشه بعد از اجرای شیدر نادیده گرفته می شود . 

این به معنای این است که سایه زن هندسی اگر آنرا انتخاب کند منجر به تولید هر (طرح) اولیه خروجی برای هر (طرح) اولیه خاص نمی شود . در واقع ارائه نوعی هرس کردن است .

همچنین به معنای این است که سایه زن هندسی توانایی تولید چندین خروجی برای هر ورودی را داراست . نکته هرچند , سایه زن هندسی برای فراهم کردن توده تشدید شده از اشکال هندسی نیست . برای مثال برای اهداف مفروش سازی کافی نیست .

هر دو نوع  ورودی و خروجی (طرح) اولیه باید در شیدر بوسیله توصیف گر layoutتعریف شوند . با فرض اینکه نوع (طرح) اولیه ورودی triangles است و نوع (طرح) اولیه خروجی line_strip است کد شیدر ما به صورت زیر است :
// geometry shaders require at least version 1.5
#version 150
 
layout (triangles) in;
layout (line_strip, max_vertices = 4) out;
...

max_vertices تعداد رئوس خروجی سایه زن هندسی را محدود می کند . این حقیقت دارد ! اگر ما سعی کنیم در خورجی رئوس بیشتری نسبت به حالت تعریف شده داشته باشیم رئوس اضافی به ادامه چرخه تولید مسیر نهایی ارسال نمی شوند.

از زمان OpenGL 4.0 , یک سایه زن هندسی توانایی درخوسات بیشتر یکی برای هر (طرح) اولیه ورودی دارد. تعداد درخواست ها بوسیله توصیفگر layout مشخص می شود . برای مثال , ما برای تعریف سایه زن هندسی که باید دوبار اجرا شود برای هر (طرح) اولیه ورودی  را بشکل زیر نوشیتم :
#version 400
 
layout (triangles, invocations = 2) in;
layout (line_strip, max_vertices = 4) out;
...
اگر invocations در layout مشخص نشده باشد , سپس شیدر یکبار برای هر (طرح) اولیه ورودی اجرا می شود .
در کنار خصوصیات رأس تعریف شده توسط کاربر , تعریف شده به عنوان خروجی در سایه زن رأس , سایه زن هندسی توانایی دسترسی به متغیر های uniform  و بافت ها را دارد .

متغیر های تعریف شده داخلی زیر فعال هستند :
in gl_PerVertex {
    vec4  gl_Position;
    float gl_PointSize;
    float gl_ClipDistance[];
} gl_in[];

in int gl_PrimitiveIDIn;
// only for OpenGL 4.0+
in int gl_InvocationID;

توجه کنید ورودی سایه زن هندسی در (struct)ساختار  gl_PerVertex  با خروجی یک بعدی ساختار در سایه زن رأس , اما در حالا در قالب یک آرایه . متغیر gl_PrimitiveID فهرست (طرح های ) اولیه ایجاد شده بوسیله دستورات Draw* را ذخیره می کند .
اگرچه سایه زن هندسی بطور مفهومی (طرح) اولیه خروجی می دهد , در عمل خروجی ما رئوس هستند. بنابراین , اگر (طرح) اولیه خروجی مثلث است سایه زن هندسی باید خصوصیات سه رأس را بنویسید . رئوس سپس به (طرح) اولیه بر حسب مقدار خروجی  layout   اسمبل می شوند برای هر رأس متغیر های داخلی ممکن است استفاده شوند :

out gl_PerVertex {
    vec4  gl_Position;
    float gl_PointSize;
    float gl_ClipDistance[];
};

out int gl_PrimitiveID;
out int gl_Layer;
// only for OpenGL 4.1+
out int gl_ViewportIndex;

همانگونه که در سایه زن رأس دیدیم , gl_Position اختیاری است اما در قسمت زیر ممکن است به آن بستگی داشته باشد برای اهداف الحاق سازی . gl_PrimitiveID باید حداقل یکبار برای برانگیختن رئوس نوشته شود . همینطور برای gl_Layer و gl_ViewportIndex , اگر مشخص شده باشد , تمام رئوس (طرح های ) اولیه باید مقادیر یکسان نوشته شود . اگر مشخص نشده باشد . برای مثال اگر سایه زن هندسی روی gl_ViewportIndex و gl_Layer چیزی ننویسد مقادیر انها صفر خواهد بود .

سایه زن های هندسی می توانند مشخص شود برای هر (طرح) اولیه, یک لایه برای ترسیم . این به سایه زن رأس اجازه مشخص کردن چندین (طرح) اولیه را می دهد , و  انها را به لایه های مختلف هدایت می کند . رندر کردن لایه ایی طرز استفاده فرم بافر ها را پیاده سازی می کند . در غیر این صورت  مقدار gl_Layer نادیده گرفته می شود . یک مثال از رندر کردن لایه ایی استفاده از ان برای نگاشت شش وجه یک مکعب در یک انتقال (pass) است .

از OpenGL 4.1 , یک سایه زن هندسی توانایی انتخاب یک دریچه دید به خروجی چندین نما که همزمان رندر می شوند با این خاصیت را دارد . 

در کنار متغیر های داخلی , سایه زن رأس ممکن است تعریف و نوشته شود به , متغیر های تعریف شده توسط کاربر. همانند شیدر های قبلی , خروجی سایه زن هندسیمی تواند به یک بافر (یا مجموعه بافر ها) منتقل شود توسط تبدیل باز خورد ( transform feedback) .

----------


## jack

سلام
وقتی با سایه زن هندسی آشنا شدم بحث ساخت سطوح از روی چند نقطه و اکسترود شدن آنها برای  خیلی جالب بود چون من حدودا یک سال روی یک پروژه فرکتالی L-System کار می کردم . هنگام خروجی گرفتن می خواستم از سایه زن هندسی استفاده کنم ولی با تعجب یک برنامه پیدا کردم که با GLU می یاد از روی یک تصویر یک شکل سه بعدی می سازه یعنی هم مثلثی می کنه هم اکسترود می کنه . حالا اگه فرصت کنم می خوام ار این برنامه برای ساخت یک رابط کاربر 3 بعدی استفاده کنم این هم لینک سایتش
http://graphicsbb.itgo.com/solutions/extrude.html
ممنون

----------


## amin1softco

والا به نظرم شیدر هندسی اومده در هسته که این کار را خودش انجام بده و



> حالا اگه فرصت کنم می خوام ار این برنامه برای ساخت یک رابط کاربر 3 بعدی استفاده کنم این هم لینک


دقیقاً می شه این را بیشتر باز کنید !! اینا چه دخلی به هم دارند ؟
*
ادامه آموزش :
*
در این صفحه ما یک مجموعه از مثال های ساده در رابطه با کارهایی که سایه زن هندسی می تواند انجام دهد تدارک دیده ایم برای نشان دادن تعدادی از قابلیت های که در قسمت قبل ذکر شد .

*مثال 1 - پاس دادن از طریق سایه زن* 
در این مثال فرض بر این است که سایه زن رأس همانند قسمت قبل باشد . 
این سایه زن نه تغییری در خصوصیاتی که از سایه زن رأس می آید ایجاد میکند نه هیچ اطلاعاتی را اضافه ویا حذف می کند این از نوع مثال های "سلام جهان"(مثال هایی برای شروع کار)برای سایه زن هندسی است .

#version 150
 
layout(triangles) in;
layout (triangle_strip, max_vertices=3) out;
 
in VertexData {
    vec2 texCoord;
    vec3 normal;
} VertexIn[3];
 
out VertexData {
    vec2 texCoord;
    vec3 normal;
} VertexOut;
 
 void main()
{
  for(int i = 0; i < gl_in.length(); i++)
  {
     // copy attributes
    gl_Position = gl_in[i].gl_Position;
    VertexOut.normal = VertexIn[i].normal;
    VertexOut.texCoord = VertexIn[i].texCoord;
 
    // done with the vertex
    EmitVertex();
  }
}

تعریف  layout نشانگر این است که ورودی ها از نوع مثلث هستند و خروجی ها از نوع مثلث نواری هستند , برای مثال هر سه رأس یک مثلث مجزا است .

سایه زن هندسی به عنوان ورودی برای هر رأس , دارای خصوصیات تعریف شده توسط کاربر برای مختصات بافت و نرمال ها, در یک بلوک به نام است که خروجی را با سایه زن رأس تطبیق می دهد . و همچنین gl_Position را دریافت می کند . 
خروجی همچنین شبیه سایه زن رأس است . بلوک به نام برای داده های رأس و gl_Position .

*مثال 2 : تکثیر هندسی* 

این سایه زن با فرض یک سایه زن رأس که مانند پاس دادن است برای مثال بدون بدون ویرایش خصوصیات رأس , چیزی مثل کد زیر می شود .

#version 410
 
in vec3 position;
in vec3 normal;
in vec2 texCoord;
 
out VertexData {
    vec2 texCoord;
    vec3 normal;
} VertexOut;
 
void main()
{
    VertexOut.texCoord = texCoord;
    VertexOut.normal = normal;
    gl_Position = vec4(position, 1.0);
}

سایه زن رأس هر (طرح) اولیه را دریافت کرده و انها را تکثیر می کند و در کپی دوم در محور X به 20 واحد جابه جا میک ند .

#version 420
 
layout(triangles) in;
layout (triangle_strip, max_vertices=6) out;
 
layout (std140) uniform Matrices {
    mat4 projModelViewMatrix;
    mat3 normalMatrix;
};
 
in VertexData {
    vec2 texCoord;
    vec3 normal;
} VertexIn[];
 
out VertexData {
    vec2 texCoord;
    vec3 normal;
} VertexOut;
 
 void main()
{
  for(int i = 0; i < gl_in.length(); i++)
  {
     // copy attributes
    gl_Position = projModelViewMatrix * gl_in[i].gl_Position;
    VertexOut.normal = normalize(normalMatrix * VertexIn[i].normal);
    VertexOut.texCoord = VertexIn[i].texCoord;
 
    // done with the vertex
    EmitVertex();
  }
  EndPrimitive();
 
  for(int i = 0; i < gl_in.length(); i++)
  {
     // copy attributes and displace copy
    gl_Position = projModelViewMatrix * (gl_in[i].gl_Position + vec4(20.0, 0.0, 0.0, 0.0));
    VertexOut.normal = normalize(normalMatrix * VertexIn[i].normal);
    VertexOut.texCoord = VertexIn[i].texCoord;
 
    // done with the vertex
    EmitVertex();
  }
  EndPrimitive();
}



در نظر داشته باشید ما EndPrimitive را در فرمان ها اضافه کردیم. این به علت است که در این مثال ما در خروجی دو (طرح) اولیه , دو مثلث نواری داریم . از این رو ما باید به شیدر بگوییم کی (طرح) اولیه تمام می شود . در واقع آخرین فرمان  EndPrimitive اختیاری است . زمانیکه سایه زن خاتمه می یابد (طرح) اولیه نیز منعقد می شود اما بهتر است آنرا اینجا داشته باشیم . 



*مثال 3 - تقسیم مثلث (به دو)* 

در مثال بعدی با فرض همان سایه زن رأس در مثال قبلی برای هر مثلث که دریافت میکنید , کد یک نوار ایجاد میکند با دو مثلث , که همان فضا را  با اضافه کردن یک رأس در نقطه میانی  مجاور مثلث اصلی اشغال میکنند .


#version 420
 
layout(triangles) in;
layout (triangle_strip, max_vertices=4) out;
 
layout (std140) uniform Matrices {
    mat4 projModelViewMatrix;
    mat3 normalMatrix;
};
 
in VertexData {
    vec2 texCoord;
    vec3 normal;
} VertexIn[];
 
out VertexData {
    vec2 texCoord;
    vec3 normal;
} VertexOut;
 
 void main()
{
     // copy attributes for the first vertex
    gl_Position = projModelViewMatrix * gl_in[0].gl_Position;
    VertexOut.normal = normalize(normalMatrix * VertexIn[0].normal);
    VertexOut.texCoord = VertexIn[0].texCoord;
    EmitVertex();
 
     // copy attributes for the second vertex
    gl_Position = projModelViewMatrix * gl_in[1].gl_Position;
    VertexOut.normal = normalize(normalMatrix * VertexIn[1].normal);
    VertexOut.texCoord = VertexIn[1].texCoord;
    EmitVertex();
 
    // this is the new vertex
    gl_Position = projModelViewMatrix * (gl_in[0].gl_Position + gl_in[2].gl_Position);
    VertexOut.normal = normalize(normalMatrix * (VertexIn[0].normal + VertexIn[2].normal));
    VertexOut.texCoord = (VertexIn[0].texCoord +  VertexIn[2].texCoord) * 0.5;
    EmitVertex();
 
     // copy attributes for the last vertex
    gl_Position = projModelViewMatrix * gl_in[2].gl_Position;
    VertexOut.normal = normalize(normalMatrix * VertexIn[2].normal);
    VertexOut.texCoord = VertexIn[2].texCoord;
    EmitVertex();
}

----------


## amin1softco

در این قسمت با دو مفهوم مهم برای درک خط تولید تصویر نهایی آشنا می شوید : تصویر گری و ترکیب . (rasterization and interpolation) اینها قسمت ثابت خط تولید تصویر هستند برای مثال آنها قابل برنامه نویسی نیستند . 
هر چند قبل از اینکه تصویر گری اتفاق بیافتد , تا اینجا چند مرحله جزئی تشریح شده است . به نام برش تبدیلات به مختصات پنجره .

*برش* 
این قسمت (طرح) اولیه اسمبل شده را دریافت می کند , با تبدیل رئوس و چک کردن اینکه در داخل حجم ناحیه برش باشد . تمام طرح های (اولیه) که داخل ناحیه برش هستند تغیری نمی کنند . (طرح)های اولیه که خارج ناحیه نما هستند در نظر گرفته نمی شوند . (طرح) های اولیه که روی مرز مشترک ناحیه برش هستند برش داده می شوند.
حجم ناحیه برش یک تبدیل از جزئی (frustum) نما آن رئوس داخل نمای جزئی هستند دارای مختصاتی شبیه هستند .



خصوصیت gl_Position از رأس مختصات رأس نگهداری می کند و برای اهداف برش تست شده است . مختصات برش رأس  است . 
برای (طرح) اولیه که دارای لبه که رئوس بروی خلاف طرف  صفحه حذف حجم برش هستند , یک رأس جدید محاسبه می شود روی نقاط مشترک برای هر صفحه برش که مشترک است . این رئوس جدید قسمتی از (طرح) اولیه خواهند شد و رئوس خارج از حجم برش در نظر گرفته نمی شوند بنابراین برش شاید رئوس جدیدی ایجاد کند روی یک (طرح) اولیه مثلث , یا برای برنامه نویس مشخص نباشد . 
شکل های زیر اتفاقی که برای یک مثلث که قسمتی از آن خارج حجم برش است را نمایش می دهد.



 رئوس جدید داری خصوصیات مرتبط درون یابی شده از رئوس لبه هستند , مگ اینکه سایه زن صاف مشخص شده باشد در تعریف خصوصیات که در این مورد مقدار خصوصیات میان لبه ها ثابت است .
رأس می تواند تبدیل شود , که همچنین شناخته می شود به عنوان پرسپکتیو قسمت (perspective division) به مختصات دستگاه نرمال شده برای مثال  

با در نظر گرفتن مختصات دستگاه نرمال شده حجم برش یک مکعب 3 بعدی دربازه -1 و 1 در تمام مختصات ها (به غیر از حالتی که عمق بسته باشد که در این حالت محدودیتی برای محور Z وجود ندارد) از این رو , یک رأس داخل حجم برش با عمق,  اگر :





*سایه تخت* :
زمانی که در تعریف خصوصیات رأس حالت تخت مشخص , خصوصیات از یک رأس به تمام رئوس باقی مانده (طرح) اولیه کپی می شود . اگر چیزی نگوییم , خصوصیات آخرین رأس  (طرح) اولیه برای همه رئوس دیگر مشخص می شود . این رأس provoking (بر انگیخته) نامیده می شود . OpenGL به ما اجازه مشخص اولین و آخرین رأس به عنوان رأس provoking با استفاده از فرمان زیر را داده :
glProvokingVertex(enum provokeMode);

بطوریکه  provokeMode می تواند مقادیر : GL_FIRST_VERTEX_CONVENTION یا GL_LAST_VERTEX_CONVENTION (این آخری به عنوان پیش فرض است) را به خود بگیرد .

*مختصات پنجره :*
مختصات پنجره الان می تواند برای هر رأس محاسبه شود . مختصات پنجره به نمای دید بستگی دارد . با در نظر گرفتن نمای دید با ارتفاع h, عرض w و مرکز در  تمام این مقادیر در مختصات پنجره هستند و واحد ها پیکسل ها است .

یک رأس با مختصات دستگاه نرمال  دارای مختصات پنجره  به صورت زیر حساب می وشد : 

بطوریکه n  و f نشانگر  مقادیر نزدیک و دور (near and far) که ممکن است با توابع خانواده glDepthRange* مشخص شوند است . اگر مشخص نشده باشد ,بطور پیش فرض n = 0 و f = 1 , و تبدیل بالا به صورت زیر ساده می شود :


*تصویر گری* :
تصویر گری فرآِند مشخص کردن مجموعه پیکسل , در تصویر نهایی , که قسمتی از (طرح) اولیه است . 
اولین مرحله تصویر گری مشخص کردن جهات است . برای مثال رو ,آیا جلو یا عقب است. اگر گلچین culling فعال باشد , تمام ان مثلث ها دارای جهت در نظر گرفته نشده صحیحی نیستند و فرآیند تصویر گری برای انها به پایان می رسد .
برای مثلث ها دارای جهت صحیح , ما مجبور به مشخص کردن پیکسل هایی که قسمتی از آن است . هر پیکسلی که مرکز آن داخل مرز های مثلث است به مجموعه پیکسل هایی که برای پردازش بیشتر است اضافه خواهد شد در شکل زیر , نقاط رنگی نمایشگر موقعیت رئوس در فضای نمایش است . توجه کنید پیکسل هایی که رئوس کاذب ممکن است قسمتی از مجموعه ی پیکسل های سازنده مثلث نباشند.


*ترکیب* 
مرحله بعدی محاسبه خصوصیات برای هر پیکسل  بر پایه خصوصیات رأس و موقعیت فاصله ی پیکسل ها در صفحه نمایش است. مختصات Barycentric استفاده می شه تا به ما در فرآیند ترکیب کمک کند هر نقطه p داخل یک مثلث (pa,pb,pc)  می تواند به صورت زیر نشان داده شود :


 که

 و




سه گانه (a, b, c) شامل مختصات گرانیگاهی barycentric برای نقطه p است . مختصات barycentric می تواند  به عنوان وزن که مشخصگر تأثیر هر رأس روی نقطه ایی داخل مثلث است دیده شود , و همانطور که قبلاً ذکر کردیم , جمع وزن ها برابر 1 است .
برای هر پیکسل , مقدار هر خصوصیت ها با هم ترکیب می شود با در نظر گرفتن نقطه p با مختصاتی که نشانگر مرکز پیکسل  با در نظر گرفتن وزن محاسبه شده است برای مثال مختصات barycentric , و مقدار های از خصوصیات در رئوس در فضای نمایش .

----------


## jack

> دقیقاً می شه این را بیشتر باز کنید !! اینا چه دخلی به هم دارند ؟


 سلام
 شما رابطه کاربر رو در یک فایل بیت مپ رسم می کنید بعد خروجی 3 بعدی از اون می گیرید. شاید بشه این کار رو با شیدر هندسی انجام داد

----------


## UfnCod3r

فکر نمیکنی یکم بیش از حد سخت شده :متفکر: 
یهو پاشدی امدی GeometryShader 
برا شما حرفه ای ها که اینا بچه بازیه ولی خب اکثرا مبتدی هستن عمرا اگه کسی ازینا چیزی سر در بیاره  :قهقهه: 
بهتر بود اول با VBO, VAO, و چند تا شیدر ساده مثل تکسچر کردن و ی نورپردازی ساده شروع می کردی
مثلا ی چی مثل این
http://tomdalling.com/blog/category/modern-opengl

بحرحال ممنون  :قلب:  (مفت باشه کوفت باشه  :بامزه:  :لبخند گشاده!: )

----------


## amin1softco

یک سایه زن قطعه ( Fragment Shader) پردازش می کند ... قطعات را . یک قطعه در اصل یک موقعیت روی پنجره (X,Y), است یک مقدار عمق  (Z) , بعلاوه تمام داده های ترکیب از قسمت قبل . 
قطعاتی که در فرآیند تصویر گری پیدا می شوند , و آن خصوصیاتی که در فاز ترکیب محاسبه می شوند , الان مصرف می شوند , یک به یک , به سایه زن قطعه داده می شوند . این سایه زن اختیاری نیست , برخلاف بازخورد تبدیل که در حال استفاده است .

سایه زن قطعه توانایی دسترسی به موقعیت قطعات, و تمام داده های ترکیبی محاسبه شده در فرآیند تصویر گری  را دارد . سایه زن محاسبات را بر اساس این خصوصیات و موقعیت پیکسل ها انجام می دهد . موقعیت  X,Y  نقاط ثابت است . برای مثال یک سایه زن قطعه نمی تواند برای نوشتن خصوصیات پیکسل های دیگر انتخاب شود . هر چند می تواند مشخصه عمق پیکسل ها را تغییر دهد (مقدار Z) 

سایه زن قطعه توانایی دسترسی به بافرفرم ها را ندارد , نه در موقعیت این پیکسل ها نه  در هر موقعیت پیکسل دیگر . سایه زن قطعه , شبیه سایه زن رأس , فقط دارای دسترسی به موقعیت پیکسل فعلی و داده های مربوط به آن اس ت. 

همانند قسمت آموزشی قبل سایه زن قطعه توانایی دسترسی به تمام uniform های مصنوعی فعال در برنامه را داراست .

تعدادی از متغییر های داخلی فعال در سایه زن قطعه در ادامه آمده (برای مشاهده لیست کامل به قسمت متغیر های داخلی در مشخصه های GLSL مراجعه کنید): 
*gl_FragCoord:* شامل مختصات قطعات  (xf, yf, zf, wf) است که (xf, yf) موقعیت پیکسل ها روی پنجره , zf عمق است و wf برابر 1/wc است . که wc قسمتی از موقعیت فضای برش قطعات است .
*gl_FrontFacing:*به ما جهت (طرح) اولیه را می دهد که مبدأ پیکسل هاست . توجه کنید اگر face culling فعال باشد این مقدار برای تمام پیکسل ها یکسان است .
*gl_PrimitiveID:ا*ین متغیر یک خروجی از شیدر هندسی است اگر هیچ شیدر هندسیی موجود نباشد این مقدار نمایانگر  فهرست (طرح) اولیه در فرمان ترسیم OpenGL است .


که فقط در نسخه 4.1 پشتیبانی می وشد .
با توجه به خروجی , این می تواند متغیر باشد , به عدد , از 1 تا GL_MAX_DRAW_BUFFERS . بطور پیش فرض یک برنامه دارای یک رنگ در خروجی و از نوع vec4 است . یک برنامه باید متغیر های تعریف شده توسط کاربر را به رخوجی های رنگ مقید کند . این ممکن است دارای بیش از یک رنگ در خروجی باشد اگر از اشیاء فرم بافر استفاده کنیم . 
سایه زن زیر ممکن است ساده ترین سایه زن قطعه ایی باشد که تا به حال نوشته اییم . اگرچه خیلی جذاب نیست ولی تمام چیز ها ار قرمز می کند !
#version 150
 
out vec4 colorOut;
 
void main()
{
    colorOut = vec4(1.0, 0.0, 0.0, 1.0);
}

همانطور که متغیر های uniform و خصوصیات رأس , متغیر های خروجی از سایه زن قطعه همچنین دارای یک مکان هستند . در سایه زن فوق , متغیر colorOut به مکان 0 (صفر) بطور پیش فرض مقید شده است . در برنامه ما  متغییر  colorOut را باید به مکان خروجی صفر مقید کنیم . مکان پیش فرض زمانی که از اشیاء فرم بافر ها استفاده نکنیم از تابع زیر استفاده می کنیم  :

void glBindFragDataLocation(GLuint program, GLuint colorNumber, const char *name);

پارامتر ها:

    program: هندل شی برنامه
    colorNumber: مکان خروجی که متغیر باید به ان مقید شود.
    name: نام متغیر خروجی سایه زن

بنابراین سایه زن فوق را در نظر بگیرید , و با فرض p به عنوان هندل به برنامه ما . ما می توانیم در برنامه خودمان دستور زیر را بنویسیم :
glBindFragDataLocation(p, 0, "colorOut");

توجه کنید مقید سازی تا زمانیکه برنامه بعدی لینک نشده است تأثیری ندارد . می توان موقعیت را بوسیله برنامه برای برنامه های پیوند شده با تابع زیر پرس و جو کرد :

GLint gGetFragDataLocation(GLuint program, const char *name);
پارامتر ها :

    program: هندل شی برنامه
    name: نام متغیر خروجی سایه زن 

اگر name به یک متغیر خروجی فعال بر نگردد , نتیجه -1 است در غیر اینصورت مکان متغیر باز می گردد.

مکان ها برای متغیر های خروجی می تواند همچنین بسختی در سایه زن بوسیله خودش با توصیف گر layout کد (نوشته) شود. تعریف متغیر colorOut می تواند شامل یک توصیفگر  layout باشد که محل آنرا ایجاد میکند . همانند مثال زیر :
layout(location = 0) out vec4 colorOut;
وقتی از توصیف گر layout در سایه زن استفاده می کنیم , مکان (location) به تنظیمات سطح بالاتر دستور glBindFragDataLocation رجوع می کند .
زمانی که از اشیاء فرم بافر استفاده میکنیم , هر خروجی درای یک مکان  (location) برای تطابق با هدف رندر (یا بافت) خروجی است, در غیر اینصورت , زمانیکه از فرم بافر پیش فرض استفاده میکنیم , تنها مکان که مهم تر است مکان صفر است .

----------


## amin1softco

> سلام
> شما رابطه کاربر رو در یک فایل بیت مپ رسم می کنید بعد خروجی 3 بعدی از اون می گیرید. شاید بشه این کار رو با شیدر هندسی انجام داد


جالبه ولی یکمی بیش از حد انتزاعی است به نظرم, ایشالا که موفق بشوید .





> فکر نمیکنی یکم بیش از حد سخت شده
> یهو پاشدی امدی GeometryShader 
> برا شما حرفه ای ها که اینا بچه بازیه ولی خب اکثرا مبتدی هستن عمرا اگه کسی ازینا چیزی سر در بیاره 
> بهتر بود اول با VBO, VAO, و چند تا شیدر ساده مثل تکسچر کردن و ی نورپردازی ساده شروع می کردی
> مثلا ی چی مثل این
> http://tomdalling.com/blog/category/modern-opengl
> 
> بحرحال ممنون  (مفت باشه کوفت باشه )


 هدف از این آموزش ها دادن یک دید کلی از خط تولید تصویر نهایی یا Pipeline کلی اپن جی ال به خواننده است اگر دقت کنید الان شکلی که در پست اولی قرار دادم الان تمام قسمت هاش در پست های بالا یی تشریح شد.. در قسمت های آموزشی بعدی نوع ها و چیز هایی که مد نظر شماست بیشتر بحث خواهد شد . البته اگر خدا بخواهد...

----------


## amin1softco

*بر پا سازی اپن جی ال * (OpenGL Setup)
این قسمت آموزشی نحوه , بار گزاری , کامپایل , و لینک  و آماده کردن سایه زن برای اجرا را نمایش می دهد . اگر هنوز شما آمادگی لازم برای نوشتن سایه زن های خود را ندارید مکان های زیادی است که می توانید آنها را از اینترنت تهیه کنید . اگرچه   فقط کمی مشکل  با آخرین نسخه های  GLSL دارید . شما همچنین می توانید سعی کنید برای  مثال های بعدی در این قسمت آموزشی .
بگذارید قبل از شروع ببینیم که آیا حداقل نسخه  OpenGL 3.3  پشتیبانی می شود . در اینجا ما قصد استفاده از  GLEW  برای دسترسی به این قابلیت را داریم . 
#include <GL/glew.h>
#include <GL/glut.h>
 
void main(int argc, char **argv) {
 
    glutInit(&argc, argv);
    ...
    glewInit();
 
    if (glewIsSupported("GL_VERSION_3_3"))
        printf("Ready for OpenGL 3.3\n");
    else {
        printf("OpenGL 3.3 not supported\n");
        exit(1);
    }
 
    setShaders();
    initGL();
 
        glutMainLoop();
}

تا جاییکه به OpenGL  بستگی دارد تنظیمات برنامه شما شبیه به یک جریان کاری نوشتن یک برنامه  C است هر سایه زن شبیه یک ماژول C است , و باید بطور مجزا کامپایل شود , همانند C . مجموعه سایه زن های کامپایل شده , سپس پیوند داده می شوند در یک برنامه , در واقع در C . 
شکل زیر نمایش دهنده مراحل ضروری برای ایجاد هر دو سایه زن  و برنامه  اختصاصی  است . در زیر قسمت بعدی این قسمت ها با جزئیات بیشتر تشریح می شوند .

----------


## amin1softco

ایجاد یک سایه زن 
شکل زیر نمایشگر مراحل ضروری برای ایجاد یک سایه زن است .

اولین مرحله ایجاد یک شی که به عنوان سایه زن عمل می کند . تابع موجود برای این هدف یک هندل برای محتوی باز می گرداند . 
سینتکس(نحو) برای این تابع به صورت زیر است :
GLuint glCreateShader(GLenum shaderType);

پارامترها:

    نوع شیدر – GL_VERTEX_SHADER, GL_GEOMETRY_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, یا GL_FRAGMENT_SHADER.

مقادیر بازگشتی:

    یک هندل سایه زن

یک برنامه سایه زن می تواند در چندین  محتوا سایه زن , تجزیه شود , هر کدام دارای چندین توابع از برنامه هستند . هر چند , اینجا فقط یک تابع main برای هر مجموعه نوع های سایه زن در هر برنامه مجزا وجود داشته باشد . 


مراحل زیر برای اضافه کردن کمی کد منبع (source code)  است . کد منبع برای هر سایه زن یک آرایه رشته ایی است , اگرچه شما می توانید از اشاره گر برای یک رشته استفاده کنید . 
سینتکس تابع برای تعیین کد منبع برای یک سایه زن به صورت زیر است :
void glShaderSource(GLuint shader, int numOfStrings, const char **strings, int *lengthOfStrings);

پارامتر ها:

    shader – هندل به یک سایه زن.
    numOfStrings – تعداد رشته ها در آرایه.
    strings – آرایه ایی از رشته ها.
    lengthOfStrings – یک آرایه با طول هر رشته , یا NULL,به معنای اینکه رشته ها با کارکتر نال پایان یافته است.

سرانجام سایه زن باید کامپایل شود تابع برای رسیدن به این هدف به صورت زیر است :
void glCompileShader(GLuint shader);

پارامتر ها:

    shader – هندل به سایه زن.

کد منبع را در نظر بگیرید که به صورت یک رشته داخل کد برنامه تعریف شده سپس قطعه کد زیر می تواند برای برپاسازی سایه زن استفاده شود .

std::string source = "#version 330\n\
\
in vec4 Color;\n\
\
void main()\
{\
    gl_FragColor = Color;\
} ";
 
GLuint f;
const char * ff = source.c_str();
 
f = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(f, 1, &ff, NULL);
glCompileShader(f);

با فرض کد داخل فایل , سپس کد منبع برای دستیابی به این هدف عبار ت است از :
char *vs;
GLuint v;
 
// get a shader handler
v = glCreateShader(GL_VERTEX_SHADER);
// read the shader source from a file
vs = textFileRead(vertexFileName);
// conversions to fit the next function
const char *vv = vs;
// pass the source text to GL
glShaderSource(v, 1, &vv,NULL);
// free the memory from the source text
free(vs);
// finally compile the shader
glCompileShader(v);

کد بالا از تابع کمکی برای خواندن نوشته استفاده می کند ,  textFileRead . کد برای این تابع اینگونه تعریف شده است :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
char *textFileRead(char *fn) {
 
    FILE *fp;
    char *content = NULL;
 
    int count=0;
 
    if (fn != NULL) {
        fp = fopen(fn,"rt");
 
        if (fp != NULL) {
 
      fseek(fp, 0, SEEK_END);
      count = ftell(fp);
      rewind(fp);
 
            if (count > 0) {
                content = (char *)malloc(sizeof(char) * (count+1));
                count = fread(content,sizeof(char),count,fp);
                content[count] = '\0';
            }
            fclose(fp);
        }
    }
    return content;
}

----------


## amin1softco

*ایجاد یک برنامه (Program)* 
شکل زیر نمایشگر مراحل ضروری برای گرفتن یک برنامه سایه زن آماده به استفاده می باشد .

اولین مرحله ساخت یک شی است که به عنوان یک برنامه محتوی است توابع حاضر برای این هدف یک هندل بر محتوی بر می گردانند .
سینتکس به صورت زیر است :
GLuint glCreateProgram(void);

مقدار بازگشتی :

    هندل برنامه

ما می توانیم هر تعداد برنامه که بخواهیم ایجاد کنیم . یکبار رندر , ما می توانیم از برنامه به برنامه در حین یک فرم مجزا , برای مثال شما ممکن بخواهید یک قوری با سایه زن های انکسار و بازتاب , در حالیکه یک مکعب که نگاشت شده را برای پس زمینه با استفاده از سایه زن دیگر استفاده می کند . 
مرحله بعدی شامل اتچ (ضمیمه) کردن سایه زن های ساخته شده در زیر قسمت قبلی به برنامه است که ما می خواهیم فقط ایجاد کنیم . سایه زن ها نیازی به کامپایل شدن در این مقطع را ندارند , آنها حتی دسترسی به کد منبع ندارند . تمام اینها برای اتچ کردن سایه زن به یک برنامه در هندلر سایه زن نیاز است . 
برای اتچ کردن یک سایه زن به یک برنامه ما از تابع زیر استفاده می کنیم :
void glAttachShader(GLuint program, GLuint shader);
پارامتر ها :

    program – هندلر به برنامه
    shader – هندلر به سایه زن
ما می توانیم چندین سایه زن از یک نوع داشته باشیم که به یک برنامه اتچ شده باشند , فقط شبیه یک برنامه C که می تواند چندین ماژول داشته باشد . برای هر نوع سایه زن اینجا فقط می توانیم یک سایه زن با کی تابع main داشته باشیم . دوباره همانند C   !.

ما همچنین می توانیم یک سایه زن را به چندین برنامه اتچ کنیم , برای مثال اگر ما تصمیم به استفاده از سایه زن رأس یکسان در چندین برنامه داشته باشیم .

آخرین مرحله لینک (پیوند) کردن برنامه است . به منظور انجام این مرحله سایه زن ها باید بطوری که در زیر قسمت قبل توضیح داده شد کامپایل شوند .

سینتکس برای تابع پیوند به صورت زیر است :
void glLinkProgram(GLuint program);
پارامتر ها :
program – یک هندلر به برنامه.

بعد از عملیات پیوند منبع سایه زن می تواند ویرایش شود و سایه زن دوباره کامپایل شود بدون اثر رو ی برنامه , برای اینکه ویرایشات اثر کند ما باید پیوند کنیم برنامه را مجدد , دوباره شبیه C .
همانطور که در شکل بالا نشان داده شده است , بعد از پیوند زدن برنامه , اینجا یک تابع برای باگذاری واقعی و استفاده برنامه , glUseProgram . هر برنامه یک هندلر نسبت داده شده دارد , و ما می توانیم داشته باشیم چندین برنامه پیوند شده و آماده برای استفاده همانطور که ما می خواهیم (و سخت افزار ما اجازه می دهد ), اما فقط یکی از آنها می تواند فعال باشد . 
سینتکس برای این تابع به صورت زیر است :
void glUseProgram(GLuint prog);

پارمتر ها :
prog – هندلر به برنامه ایی که شما می خواهد استفاده کنید .

توجه کنید که فرخوانی  glUseProgram با پارامتر ها صفر , اگرچه ممکن است , باعث رها شدن سایه زن ها به حالت تعریف شده باشد , که فرآیند جالبی نیست. یک چیز دیگر , برنامه باید با موفقیت پیوند شود تا قابل استفاده باشد . 

سایه زن هندسی و هر دو مفروش سازی , اختیاری هستند , از این رو برای پیوند موفقیت آمیز برنامه لازم نیستند . سایه زن رأس وسایه زن قطعه الزامی هستند مگر اینکه تبدیل بازخورد استفاده شده باشد که در این مورد سایه زن قطعه لازم نیست . 

اگر یک برنامه در حال استفاده باشد و دوباره پیوند شود , بصورت خودکار در مکان استفاده قرار می گیرد بنابراین این حالت فرخوانی مجدد تابع لازم نیست.
کد زیر تمام عملیات های لازم را اجرا می کند , با فرض هندلر های سایه زن s1 و s2 همانند قسمت قبل تعریف شده باشند .
GLuint p;
 
p = glCreateProgram();
 
glAttachShader(p,s1);
glAttachShader(p,s2);
 
glLinkProgram(p);
...
// and later on
glUseProgram(p);


*بر پا سازی مثال* 
کد منبع زیر شامل تمامی مراحل شرح داده قبلی با در نظر گرفتن یک برنامه با سه نوع سایه زن : رأس , قطعه , هندسی است . متغیر p از نوع GLuint,  بصورت کلی تعریف شده .
void setShaders() {
 
    GLuint v, g, f;
    char *vs,*gs, *fs;
 
    // ساخت هندل های برنامه
    v = glCreateShader(GL_VERTEX_SHADER);
    g = glCreateShader(GL_GEOMETRY_SHADER);
    f = glCreateShader(GL_FRAGMENT_SHADER);
 
    // خواندن کد منبع از فایل 
    vs = textFileRead("example.vert");
    gs = textFileRead("example.geom");
    fs = textFileRead("example.frag");
 
    const char * vv = vs;
    const char * gg = gs;
    const char * ff = fs;
 
    // مشخص کردن منبع سایه زن ها
    glShaderSource(v, 1, &vv,NULL);
    glShaderSource(g, 1, &gg,NULL);
    glShaderSource(f, 1, &ff,NULL);
 
    free(vs);free(gs);free(fs);
 
    // کامپایل تمام سایه زن ها
    glCompileShader(v);
    glCompileShader(g);
    glCompileShader(f);
 
    // ساخت برنامه
    p = glCreateProgram();
 
    // اتچ کردن سایه زن ها به برنامه
    glAttachShader(p,v);
    glAttachShader(p,g);
    glAttachShader(p,f);
 
    // پیوند و تعیین برنامه برای استفاده
    glLinkProgram(p);
    glUseProgram(p);
}

----------


## amin1softco

*عیب یابی : ثبت اطلاعات (Infolog)*
عیب یابی یک سایه زن خیلی سخت است . هنوز هیچ دستور printf موجود نمی باشد و هرگز نخواهد بود اگرچه در آینده ابزار های برنامه نویسان با چنین قابلیتی برای عیب یابی موجود خواهد بود . این موضوع که شما می تواندی از بعضی از ترفند ها استفاده کنید حقیقت دارد ولی اینها در هر صورت قابل چشم پوشی نیستند  .
در این بخش بعضی از توابع برای چک کردن اینکه کد شما کامپایل شده است و با موفقیت پیوند شده ارائه شده است . 
هر دو کامپایلر و پیوند دهنده  linker, هنگامیکه خطائی پیدا می شوند در خروجی پیغام هایی می دهند در کنار اینکه این هشدار ها به ما در حل مشکلات کمک میکند . طبیعت اینگونه پیغام ها خیلی شبیه کامپایلر ها و پیوند دهنده ها برنامه  C می باشد .
وضعیت مراحل کامپایل را می توان با تابع زیر پرس و جو کرد :

void glGetShaderiv(GLuint object, GLenum type, int *param);

پارامتر ها:

    object – هندل به شی . چه یک سایه زن یا یک برنامه باشد ;
    type – GL_COMPILE_STATUS;
    param –مقدار بازگشتی, GL_TRUE اگر صحیح بود, GL_FALSE در غیر اینصورت.

وضعیت مرحله پیوند را یم توان با استفاده از تابع زیر پرس و جو کرد :
void glGetProgramiv(GLuint object, GLenum type, int *param);

پارامتر ها:

    object – هندل به شی, یک برنامه اگر به دنبال اطلاعات پیونددهی می باشیم ;
    type – GL_LINK_STATUS;
  param –مقدار بازگشتی, GL_TRUE اگر صحیح بود, GL_FALSE در غیر اینصورت.

زمانیکه GL_FALSE را در هر یک از توابع بالا گرفتیم گرفتن اطلاعات بیشتر  تو سط دستور InfoLog امکان پذیر است این لاگ اطلاعات درباره آخرین عملیات اجرا شده در هر شی را ذخیره می کند , هم اطلاعات سایه زن ها و هم برنامه ها از قبیل هشدار های , خطا ها در هنگام کامپایل کردن اشاره کرد و مشکلاتی که در هنگام مرحله پیوند دهی ایجاد می شود . لاگ همپنین می تواند به شما بگوید که اگر سایه زن در نرم افزار اجرا خواهد شد به این معنی که سخت افزار شما بعضی از ویژگی هایی که شما استفاده می کنید را پشتیبانی نمی کند و یا حتی در حالتی ایده آل تر سخت افزار های دیگر را هیچ خصوصیتی برای پیغام های infolog موجود نمی باشد بنابراین درایور ها و یا سخت افزار های مختلف ممکن است لاگ های مختلف ایجاد کنند .
به منظور گرفتن infolog برای یک سایه زن و یا برنامه بخصوص ما می توانیم از توابع زیر استفاده کنیم :
void glGetShaderInfoLog(GLuint object, int maxLen, int *len, char *log);
void glGetProgramInfoLog(GLuint object, int maxLen, int *len, char *log);

پارامتر ها:

    object – هندل به یک شی . یا یک سایه زن یا یک برنامه;
    maxLen – بیشترین مقدار کارکتر هایی که باید از info log گرفته شود .
    len – بازگرداندن مقدار طول واقعی گرفته شده از infoLog;
    log –لاگ به خودی خود.

توجه کنید که شما باید طول infolog را برای بازگرفتن آن بدانید. برای پیدا کردن این ذره ارزشمند اطلاعات از توابع زیر استفاده کنید (در زبان نشانه گذاری اپن جی ال):
void glGetShaderiv(GLuint object, GLenum type, int *param);
void glGetProgramiv(GLuint object, GLenum type, int *param);

پارامتر ها:

    object – هندل به شی یا سایه زن یا یک برنامه;
    type – GL_INFO_LOG_LENGTH;
    param – مقدار بازگشتی, طول InfoLog.

توابع زیر را می توان برای بازگرفتن و چاپ محتوای موجود در infolog بکار برد :
void printShaderInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;
 
    glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &infologLength);
 
    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
    printf("%s\n",infoLog);
        free(infoLog);
    }
}
 
void printProgramInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;
 
    glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &infologLength);
 
    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
    printf("%s\n",infoLog);
        free(infoLog);
    }
}

----------


## amin1softco

در _زیر قسمت_ قبلی یک تابع برای اتچ کردن سایه زن به یک برنامه نمایش داده شد . یک تابع برای خارج کردن اتچ (دیتچ)سایه زن از یک برنامه همچنین موجود است . 
سینکس به صورت زیر است :
void glDetachShader(GLuint program, GLuint shader);

پارامتر ها:

    program – برنامه برای دیتچ کردن;
    shader – سایه زن برای دیتچ کردن.

فقط سایه زن هایی که اتچ نشده اند بطور موثر حدف می شوند پس این عملیات بی ربط نیست . برای حذف یک سایه زن , یا یک برنامه , از توابع زیر استفاده کنید :
void glDeleteShader(GLuint id);
void glDeleteProgram(GLuint id);

پارامترها:

    id – هندل سایه زن یا برنامه که باید حذف شود .

در مواردی که سایه زن هنوز به یک یا چند برنامه اتچ است , سایه زن در واقع حذف نشده اما فقط برای حذف علامت خورده است . عملیات حذف زمانی اتفاق می افتد که دیگر به هیچ برنامه ایی اتچ نباشد برای مثال سایه زن از تمام برنامه ها یی که اتچ شده دیتچ شود . 

این موردی را برای حدف زود هنگام سایه زن ها ایجاد میکند. اگر یک برنامه بوسیله ساخت تمام برنامه ها شروع شود و به تمام سایه  زن ها اتچ شود برای ساخت خط تولید تصویر نهایی , سپس سایه زن ها به محض اینکه به برنامه اتچ شوند حذف می شوند . در حال اتصال شده ها در واقع مانع از حدف سایه زن ها می شوند . , علامت گذاری آنها برای حذف .زمانیکه یک برنامه شروه می وشد آن تمام پروسه ها ی مورد نیاز برای حذف برنامه ها , و همینکه یک سایه زن دیگر به هیچ برنامه ایی متصل نباشد بصورت خودکار خذف می شود . من نمی دانم این کار چگونه پیاده سازی شده و چگونه در عمل کار می کند و لی این حرف ها روی کاغذ جذاب تر است .

----------


## amin1softco

*انواع داده ها* 
نوع های داده ایی ساده زیر در GLSL موجود است :

floatdoubleboolintuint

اینها نوع های مهمول در C هستند به جز bool.

بردار ها با 2و3یا 4 جز همچنین  برای هر نوع ساده داده ای که در بالا ذکر شد موجود هستند. و به صروت زیر تعریف می شوند :

    vec{2,3,4} یک بردار از 2, 3, یا 4, ممیز شناور
    dvec{2,3,4} برداری از ممیز شناورdouble
    bvec{2,3,4} بردار از نوع بولین bool
    ivec{2,3,4} بردار از اعداد صحیح 
    uvec{2,3,4} بردار از اعداد صحیح بدون علامت
ماتریس های مربعی 2×2, 3×3 و 4×4 برای ممیز شناور ها و دوبل ممیز شناور (floatsو doubles) فراهم شده اند زمانیکه آنها به وفور در کار های گرافیکی استفاده می شوند . نوع داده های نسبی عبارتند از :

    mat2, dmat2
    mat3, dmat3
    mat4, dmat4
همچنین ماتریس های غیر مربعی , همچنین برای  ممیز شناور ها و دوبل ممیز شناور که دارای فرم عمومی هستند :

    mat{2,3,4}x{2,3,4}
    dmat{2,3,4}x{2,3,4}
اگر ستون ها و ردیف ها ,اعداد اول و دوم به ترتیب , با هم برابر باشند این با تعریف قبلی ماتریس مربعی یکسان است . 
یک مجموعه از نوع های خاص برای دسترسی به بافت ها موجود هستند که اینها نمونه گیر sampler نامیده می شوند و لازم است به مقدار بافت ها دسترسی داشته باشند که همچنین با نام تکسل ها ( texels ) شناخته می شوند. 

تعدادی از نوع های معمول برای نمونه گیری از بافت عبارتند از :

    sampler1D – برای 1D بافت های
    sampler2D – برای 2D بافت های
    sampler3D – برای 3D بافت های
    samplerCube – برای نگاشت مربع 
    sampler2DShadow – برای نگاشت سایه ها
شمارشگر اتمی همچنین یک ویژگی جدید در سخت افزار های OpenGL 4 است . به آموزش مربوط به شمارشکر اتمی برای اطلاعات بیشتر مراجعه کنید .

در GLSL , آرایه ها همانند سینتکس زبان C تعریف می شوند هرچند , آرایه ها نمی توانند در زمان تعریف مقدار گذاری شوند . دسترسی به عناصر آرایه مثل زبان C انجاممی شود .

ساخت ها همچنین در GLSL قابل تعریف است و سینتکس آن همانند زبان C است . 
struct dirlight {
 
    vec3 direction;
    vec3 color;
};

*متغیر ها* 
تعریف یک متغیر ساده کاملاً شبیه زبان C است , ما حتی در زمان تعریف می توانیم متغیر را مقدار گذاری کنیم .
float a,b;      // two vector (yes, the comments are like in C)
int c = 2;      // c is initialized with 2
bool d = true;  // d is true

تعریف نوع های داده ایی دیگر از متغیر ها از همین الگو پیروی می کند .GLSL بر پایه سازنده ها برای مقدار دهی اولیه و کَست (cast تبدیل نوع ها) است . هرچند برای تبدیل های ضمنی از یک سیاست راحت استفاده می کند . یک نوع می تواند  بطور ضمنی به بیشتر نوع های دیگر تبدیل شود . برای مثال تبدیل عدد صحیح به ممیز شناور  int2float:
float b = 2;        // implicit conversion
int a = 2;
float c = float(a); // also correct. c is 2.0
 
vec3 f;     // declaring f as a vec3
vec3 g = vec3(1.0,2.0,3.0); // declaring and initializing g

GLSL کاملاً انعطاف پذیر است زمانیکه یک متغیر توسط متغیر دیگر مقدار گذاری می شود . تنها نیاز است که مشا اعداد ضروری از جز ها را مشخص کنید . به مثال زیر نگاه کنید :
vec2 a = vec2(1.0,2.0);
vec2 b = vec2(3.0,4.0);
 
vec4 c = vec4(a,b) // c = vec4(1.0,2.0,3.0,4.0);
 
vec2 g = vec2(1.0,2.0);
 
float h = 3.0;
 
vec3 j = vec3(g,h);

ماتریس ها همچنین از این قاعده پیروی می کنند . شما یک تنوع گسترده از سازنده ها دارید برای ماتریس ها . برای مثال سازنده زیر برای ایجاد یک ماتریس موجود است :
mat4 m = mat4(1.0) // initializing the diagonal of the matrix with 1.0
 
vec2 a = vec2(1.0,2.0);
vec2 b = vec2(3.0,4.0);
 
mat2 n = mat2(a,b); // matrices are assigned in column major order
 
mat2 k = mat2(1.0,0.0,1.0,0.0); // all elements are specified

تعریف و مقدار گذاری برای ساختار ها بصورت زیر نشان داده شده :
struct dirlight {       // type definition
    vec3 direction;
    vec3 color;
};
 
dirlight d1;
 
dirlight d2 = dirlight(vec3(1.0,1.0,0.0),vec3(0.8,0.8,0.4));

در GLSL تعدادی از اضافات هستند که برای راحت کردن زندگی ما تدارک دیده شده اندو باعث می شوند کد کمی تمیز تر باشد . دسترس به یک بردار می تواند بوسیله حروف مانند انتخابگر های C استاندارد انجام شود .
vec4 a = vec4(1.0,2.0,3.0,4.0);
 
float posX = a.x;
float posY = a[1];
 
vec2 posXY = a.xy;
 
float depth = a.w

همانطور که در قطعه کد قبل نشان داده شد , امکان استفاده از حروف x,y,z,w برای دسترسی به اجزاء بردار ها وجود دارد اگر شما در مورد رنگ ها صحبت کنید حروف r,g,b,a می تواند استفاده شود. برای مختصات بافت ها انتخابگرهای s,t,p,q موجود هستند . 
توجه کنید , بوسیله تبدیل , مختصات بافت اغلب بعنوان s,t,r,q است هرچند r بهعنوان یک انتخابگر برای رنگ قرمز در  RGBA است از این رو ما نیاز داریم تا تفاوت حروف را تمییز دهیم و از خوشبختی یکی p است . 
انتخابگر ماتریس می تواند یک یا دو آرگومان داشته باشد برای مثال m[0], یا m[2][3]   در اولین مورد اولین ستون انتخاب شده  , در حالیکه در دومی یک عنصر تنها انتخاب شده است .
همانند ساختار ها نام های عناصر از ساختار ها می تواند در C استفاده شود . بنابراین با فرض ساختار تشریح شده در بالا  کد زیر می تواند نوشته شود :

d1.direction = vec3(1.0,1.0,1.0);


*توصیفگر ثابت ها*
یک تعریف از متغیر محلی که همچنین می تواند استفاده شود کلمه const است همانند :
const int gravity = 9.8;

----------


## amin1softco

*جملات کنترل جریان* 
انتخاب های موجد کاملاً شبیه C است . جملات شرطی شبیه if-else , حلقه ها شبیه  for, while و do-while.
if (bool expression)
    ...
else
    ...

for (initialization; bool expression; loop expression)
    ...

while (bool expression)
    ...

do
    ...
while (bool expression)


کمی پرش هم تعریف شده :

    continue –موجود در حلقه ها , موجب پرش به تکرار بعدی در حلقه
    break – برای خارج شدن از حلقه ها
    discard
کلمه discard فقط در سایه زن قطعه قابل استفاده است که موجب پایان یافتن سایه زن روی قطعه فعلی بدون نوشتن در فرم بافر یا عمق می شود. 

*توابع* 

همانند C یک سایه زن می تواند درون توابع تعریف شود .هر نوع سایه زن باید دارای یک تابع main با سینتکس زیر باشد :
void main()

توابع تعریف شده توسط کاربر ممکن است نوشته شود . همانند C یک تابع ممکن است دارای مقدار بازگشتی باشد و باید از جمله return برای انتقال خروجی به بیرون استفاده کند . در GLSL یک تابع همچنین پارامتر هایش را به عنوان خروجی از یک تابع تعریف کند . نوع خروجی می تواند هر چیزی باشد بجز آرایه ها !.
پارامتر های توابع دارای توصیفگر های موجود زیر است :

    in – برای پارامتر ها ورودی 
    out – برای خروجی تابع. جمله return نیز یک گزینه برای ارسال نتیجه تابع است.
    inout – برای پارامتر هایی که هم ووردی و هم خروجی هستند در توابع.

اگر هیچ توصیفگری مشخص نشود بطور پیش فرض in در نظر گرفته می شود.
چند نکته آخر :
یک تابع می تواند تا زمانیکه لیست پارامتر های آن متفاوت است اور لود شود .
رفتار بازگشتی در مشخصه ها تعریف نشده است !

یک مثال از یک تابع برای پایان این زیر بخش :
vec4 toonify(in float intensity) {
 
    vec4 color;
    if (intensity > 0.98)
        color = vec4(0.8,0.8,0.8,1.0);
    else if (intensity > 0.5)
        color = vec4(0.4,0.4,0.8,1.0);
    else if (intensity > 0.25)
        color = vec4(0.2,0.2,0.4,1.0);
    else
        color = vec4(0.1,0.1,0.1,1.0);     
 
    return(color);
}

----------


## amin1softco

زیر روال ها 
زیر روال ها یک مکانیسم در اپن جی ال هستند که اجازه پیکر بندی پویا از رفتار سایه زن بدون نیاز به ساخت مجدد برنامه (rebuild)را می دهند .
فرض کنید ما می خواهیم یک سایه زن را پیکر بندی کنیم که یک بازه بزرگ رفتار ها را فراهم می کند . یک سناریو نوعی نور پردازی است . ما می توانیم یک سایه زن مجزا بنویسیم که چندین الگوریتم نورپردازی و در میان استفاده از متغیر های uniform ,  که الگوریتم مورد استفاده در هر زمان را کنترل کنیم . 
دلیل که چنین برپاسازیی ممکن است بهبود عملکرد باشد . برای مثال در زمان اجرا ما می توانیم بهترین الگوریتم را انتخاب کنیم که حداقل به کمترین سطح پیش-تعریف شده برای FPS  دست پیدا می کنیم .

از انجاییکه هدف ما فقط نشان دادن استفاده از زیر روال ها است ما قصد داریم یک مثال بشدت ساده که ما می خواهیم یکی از دو رنگ ممکن را مشخص کند بزنیم . خواه آبی یا قرمز , بر اساس مقدار متغیر uniform . یک سایه زن ساده برای دستیابی به این هدف می تواند :
#version 400
 
layout (std140) uniform Matrices {
    mat4 pvm;
} ;
 
in vec4 position;
 
out vec4 color;
 
uniform int redBlueFlag;
 
void main()
{
    if (redBlueFlag == 1)
        color = vec4(1.0, 0.0, 0.0, 1.0);
    else
        color = vec4(0.0, 0.0, 1.0, 1.0);
 
    gl_Position = pvm * position ;
}

ما می توانیم همچنان از توابع برای مشخص کردن رنگ به صورت زیر استفاده کنیم :
#version 400
 
layout (std140) uniform Matrices {
    mat4 pvm;
} ;
 
in vec4 position;
 
out vec4 color;
 
uniform int redBlueFlag;
 
vec4 redColor() {
 
    return vec4(1.0, 0.0, 0.0, 1.0);
}
 
vec4 blueColor() {
 
    return vec4(0.0, 0.0, 1.0, 1.0);
}
 
void main()
{
    if (redBlueFlag == 1)
        color = redColor();
    else
        color = blueColor();
 
    gl_Position = pvm * position ;
}


این نگاه خاص به نتیجه نهایی یک سادگی می دهد که بنظر می رسد کمی زیاده روی کردیم . , اما همانطور که قبلاً دکر کردیم هدف فقط تولید یک سینکس برای زیر روال ها است . 

وارد زیر وارد ها شوید . ما قصد داریم یک زیر روال تعریف کنیم برای هر گزینه رنگ که ما داریم . اول ما باید یک امضاء signature تعریف کنیم . در این مورد ما یک تابع بدون آرگومان داریم که یک  vec4 بر می گرداند . سپس ما تعریف می کنیم توابعی که ما قصد داریم در تعریف ای نامضاء استفاده کنیم .
// the signature
subroutine vec4 colorRedBlue ();
 
// option 1
subroutine (colorRedBlue ) vec4 redColor() {
 
    return vec4(1.0, 0.0, 0.0, 1.0);
}
 
// option 2
subroutine (colorRedBlue ) vec4 blueColor() {
 
    return vec4(0.0, 0.0, 1.0, 1.0);
}

توجه کنید زمانیکه ما یک امضاء تعریف میک نیم ما کی نام فراهم کرده اییم  , colorRedBlue. این یک نوع برای نام زیر نوع است . زمانیکه زیر روال ها را می نویسیم از کلمه کلیدی subroutine استفاده می کنیم و اسم نوع را , colorRedBlue , درون پرانتز ها , برای مشخص کردن نوع زیر روال ها . در کنار این جزئیات هر چیزی برای توابع تعریف شده توسط کاربر یکتاست .

ما یه چیزی بیشتر نیاز داریم , یک متغیر uniform خاص برای کنترل انتخابی  که باید استفاده شود . این به عنوان متغیر subroutine uniform  تعریف می شود . این متغیر در برنامه مشخص می شود سمتی که ما بعداً خواهیم دید . 

subroutine uniform colorRedBlue myRedBlueSelection;

حالا ما می توانیم تابع main خود را به صورت زیر بنویسیم :
void main()
{
    color = myRedBlueSelection();
    gl_Position = pvm * position ;
}

همانطور که ما می بینیم عبارت if رفته است, و تابع main ساده تر شده است . استفاده از زیر روال ها باعث تمیز تر شدن کد ها می شود . از طرف دیگر ممکن است دیباگ کردن می شود زمانیکه یک مورد دیگر برای پیگیری وجود دارد .

ما می توانیم چندین زیر روال داشته باشیم , و هر کدام می توانند چندین تابع تعریف شده داشته باشند . در واقع , تا زمانیکه امضاء ها سازگار هستند ما می توانیم یک تابع که بتواند در نوع های مختلف استفاده شود بنویسم .

subroutine vec4 colorRedBlue();
subroutine vec4 colorRed();
 
subroutine (colorRedBlue, colorRed ) vec4 redColor() {
 
    return vec4(1.0, 0.0, 0.0, 1.0);
}

از طرف برنامه ما مجبوریم زیرروالی که قصد استفاده از آنرا داریم انتخاب کنیم به هر زیر روال یک شاخص اختصاص می دهیم , و دریافت این شاخص توسط تابع زیر انجام می شود :
GLuint glGetSubroutineIndex(GLuint program, GLenum shaderType, const GLchar *name);

پارامتر ها:

    program: اسم برنامه 
    shaderType:  قسمت سایه زن زمانیکه روتین تعریف شده باید به یکی از مقادیر زیر مشخص شود :  GL_VERTEX_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_GEOMETRY_SHADER, یا GL_FRAGMENT_SHADER
    name: نام روتین

ما همچنین نیاز به پرس و جو مکان زیر روال ها بوسیله تابع زیر میکنیم :
GLint glGetSubroutineUniformLocation(GLuint program, GLenum shaderType, const GLchar *name);

پاارمتر ها:

    program: نام برنامه
    shaderType:  قسمت سایه زن زمانیکه روتین تعریف شده باید به یکی از مقادیر زیر مشخص شود :  GL_VERTEX_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_GEOMETRY_SHADER, یا GL_FRAGMENT_SHADER
    name: نام زیر روال  uniform

برگردید به مثال خودمان , ما می توانیم بنویسیم :
GLuint routineC1 = glGetSubroutineIndex(p, GL_VERTEX_SHADER, "redColor");
GLuint routineC2 = glGetSubroutineIndex(p, GL_VERTEX_SHADER, "blueColor");
GLuint v1 = glGetSubroutineUniformLocation(shader.getProgramIn  dex(), GL_VERTEX_SHADER, "myRedBlueSelection");

سرانجام ما نیاز داریم مشخص کنیم کدام زیر روال ها به هر نوع (زیر روال) اختصاص داده شده داریم . زمانیکه ما می توانیم چندین نوع داشته باشیم . ما نیاز به یک آرایه داریم که شاخص آرایه مکان زیرروال uniform است , و مقدار آن زیر روال است . تابعی که این راتباط را برقرار می کند :
GLint glUniformSubroutinesuiv(GLenum shaderType, , GLsizei count, const GLuint *indices);

پارامتر ها:

    shaderType:  قسمت سایه زن زمانیکه روتین تعریف شده باید به یکی از مقادیر زیر مشخص شود :  GL_VERTEX_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_GEOMETRY_SHADER, یا GL_FRAGMENT_SHADER
    count: تعداد عناصر آرایه
    name: آرایه ای برای مشخص کردن ارتباط بین مکان زیرروال های uniform و شاخص آن مشارکت دارد .

توجه کنید که برنامه باید قبل از فراخوانی تابع بالا استفاده شود . علاوه براین , مشارکت قسمتی از حالت برنامه نیست , و به محض پیوند مجدد یا فراخوانی glUseProgram, glBindProgramPipeline, یا glUseProgramStages از بین می رود .
در عمل , این پیاده سازی ما باید از glUniformSubroutinesuiv بین فراخوانی glUseProgram و دستورات ترسیمی استفاده کنیم . 
قطعه کد  OpenGL زیر نمایش دهنده چگونگی دریافت اطلاعات درباره زیر روال ها  uniform و زیر روال های سازگار آن است . این قطعه نمایشگر تعدادی از توابع  که توسط اپن جی ال برای پرس و جو اطلاعات درباره زیر روال ها تدارک دیده شده است .

int maxSub,maxSubU,activeS,countActiveSU;
char name[256]; int len, numCompS;
 
glGetIntegerv(GL_MAX_SUBROUTINES, &maxSub);
glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &maxSubU);
printf("Max Subroutines: %d  Max Subroutine Uniforms: %d\n", maxSub,maxSubU);
 
glGetProgramStageiv(p, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &countActiveSU);
 
for (int i = 0; i < countActiveSU; ++i) {
     
    glGetActiveSubroutineUniformName(p, GL_VERTEX_SHADER, i, 256, &len, name);
 
    printf("Suroutine Uniform: %d name: %s\n", i,name);
    glGetActiveSubroutineUniformiv(p, GL_VERTEX_SHADER, i, GL_NUM_COMPATIBLE_SUBROUTINES, &numCompS);
     
    int *s = (int *)malloc(sizeof(int) * numCompS);
    glGetActiveSubroutineUniformiv(p, GL_VERTEX_SHADER, i, GL_COMPATIBLE_SUBROUTINES, s);
    printf("Compatible Subroutines:\n");
    for (int j=0; j < numCompS; ++j) {
     
        glGetActiveSubroutineName(p, GL_VERTEX_SHADER, s[j], 256, &len, name);
        printf("\t%d - %s\n", s[j],name);
    }
    printf("\n");
    free(s);
}

قطعه کد بالا نتیجه زیر را تولید می کند :
Max Subroutines: 1024  Max Subroutine Uniforms: 1024
Suroutine Uniform: 0 name: myGreenSelection
Compatible Subroutines:
        2 - doNotUseGreen
        3 - useGreen

Suroutine Uniform: 1 name: myIntensitySelection
Compatible Subroutines:
        4 - halfIntensity
        5 - fullIntensity

Suroutine Uniform: 2 name: myRedBlueSelection
Compatible Subroutines:
        0 - blueColor
        1 - redColor

----------


## amin1softco

*ارتباطات برنامه => سایه زن* 
خط تولید سایه رن رئوس را به عنوان ورودی دریافت می کند . این رئوس دارای یک یا چند خصوصیت هستند . علاوه براین اپن جی ال اجازه تعریف متغیر هایی که مشارکتی در رئوس ندارند می دهد اما آنها نیازمند تأثیر سایه زن دارند . یک مثال برای چنین متغیری موقعیت نور ها می باشد . 

از نقطه نظر GLSL متغیر ها در دو شکل هستند :

    attributes خصوصیات 
    uniforms یکتا ها
یک متغیر uniform برای تمام رئوس در یک فراخوانی اپن جی ال ثابت است. ممکن است همانند متغیر سراسری که مقادیر انها برای هر رأس تغیری نمی کند در فراخوانی اپن جی ال به نظر برسد . مثال هایی از این نوع متغیر ها ماتریس های تبدیل , خصوصیات نور , تنظیمات ابر fog , و متغیر هایی از قبیل جاذبه و سرعت , غیره ..

متغیر های Uniform می توانند همچنین در بلاک هایی که اجازه برپاسازی موثر تر داده ها و انتقال از یک برنامه به سایه زن را دارا هسند برپا شوند .
نوع دوم :
یک خصوصیت برای یک رأس مشخص شده است , و بطور معمول از رأس به رأس متغیر است . واضح ترین مثال موقعیت رأس است . مختصات بافت و نرمال ها همچنین در زمره خصوصیات معمولی رأس است . این ها در بافر نوشته می شود و به یک خصوصیت سایه زن خاص مقید می شود .

----------


## amin1softco

*متغیر های خصوصیت* 
همانطور که در قسمت قبل ذکر کردیم , رئوس دارای خصوصیاتی هستند که باید از خط تولید گرافیکی تغذیه شوند. برای رسم یک شی ما نیاز به مشخص کردن رئوس داریم , و چگونگی ارتباط آنها برای تعریف صورت ها داریم . علاوه بر این , رئوس دارای خصوصیات هستند , در کنار موقعیت , به نام نرمال و مختصات بافت . 

برای ارسال داده های رأس به سایه زن ما مجبوریم  مکان خصوصیات را در سایه زن رأس بدانیم . OpenGL به ما اجازه تعریف مکان خاص برای هر خصوصیت را می دهد . راه ممکن دیگر این است که به OpenGL اجازه بدهیم مکان ها را تعریف کند و بعد از آن پرس و جو را سپس تعریف کند . 

برای مشخص کردن یک مکان برای یک خصوصیت ما باید قبل از پیوند برنامه , یا , اگر برنامه برنامه هم اکنون پیوند خورده , دوباره انرا پیوند بزنیم . 
 سایه زن گلچین شده زیر را در نظر بگیرید ":
#version 330
 
in vec3 position;
in vec3 normal;
in vec2 texCoord;
...
تابع glBindAttribLocation برای مقید کردن یک مکان به یک خصوصیت بکار می رود .
void glBindAttribLocation( GLuint program, GLuint index, const GLchar *name);

پارامتر ها:

    program: هندل شی برنامه
    index: فهرست که که خصوصیات به آن مقید شده اند . 
    name: نام خصوصیت رأس 

با در نظر گرفتن گلچین فوق , و یک هندل شی برنامه p , ما می توانیم خصوصیت “position را به مکان 0 با خط زیر مقید کنیم :

glBindAttribLocation(p, 0, "position");

فراموش نکنید برنامه را بعد از مقید کردن مکان به خصوصیت پیوند بزنید .
گزینه دیگر این است که به OpenGL اجازه انتخاب یک مکان مناسب و پرس و جو بعد از پیوند خوردن برنامه را بدهیم . تابع پرس و جو glGetAttribLocation است . 
GLint glGetAttribLocation( GLuint program, const GLchar *name);

پارامتر ها:

    program: هندل شی برنامه
    name: نام خصوصیت رأس 

مقدار بازگشتی:

    مکانی که خصوصیت به آن مقید شده است .

مثال : با فرض p به عنوان هندل شی برنامه :
GLint vertexLoc;
 
glLinkProgram(p);
vertexLoc = glGetAttribLocation(p,"position");

سپس vertexLoc مکان خصوصیت “position”را ذخیره می کند .

حالا ما می دانیم مکان استقرار خصوصیات را می دانیم , و آرایه هایمان تعریف شده است , ما قصد داریم ببینیم چگونه مقادیر را برای این خصوصیات به خط تولید گرافیکی  ارسال کنیم .

 برای تعریف این خصوصیات , ما در ابتدا داده ها در یک آرایه قرار می دهیم . اینجا ما قصد داریم یک آرایه برای هر خصوصیت  در نظر بگیریم , اگرچه این می تواند همچنین در یک آرایه قرار گیرد . 

// Data for a set of triangles
float pos[ ] = {-1.0f, 0.0f, -5.0f, 1.0f,
1.0f, 0.0f, -5.0f, 1.0f,
0.0f, 2.0f, -5.0f, 1.0f, …};
 
float textureCoord[ ] = { … };
 
float normal[ ] = { … };

شاخص i  را برای تمام آرایه ها در نظر بگیرید  ما مجموعه از خصوصیات را برای رأس i   دریافت می کنیم .
تعریف یک آرایه از شاخص ها به ما اجازه اتصال این رئوس را به صورت دلخواه برای ساخت (طرح)اولیه می دهد. بنابراین ما می توانیم آرایه زیر را با شاخص ها اضافه کنیم . 
unsigned integer index[ ] = {0, 1, 2, …};

بدون شاخص آرایه , سایه زن رئوس را به صورت پی در پی در نظر می گیرد .

هر خصوصیت در یک بافر OpenGL قرار می گیرد . مجموعه بافر ها قسمتی از یک OpenGL Vertex Array Object, یا VAO هستند.

ابتدا ما کی VAO می سازیم و آنرا مقید می کنیم :
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

برای هر خصوصیت اختصاصی , ما فرایند زیر را انجام می دهیم :
 ابتدا ما یک بافر ایجاد میک نیم 
آنرا مقید می کنیم 
آنرا با داده ها پر می کنیم 
و سرانجام آنرا با خصوصیات ورودی خط تولید گرافیکی مشارکت می دهیم .(مربوط میکنیم)

برای مثال , آرایه pos را در نظر بگیرید , و خصوصیت  “position” , ما می توانیم فرآیند را به صورت زیر انجام دهیم :
GLuint buffer;
glGenBuffers(1, &buffer);
 
// در یافت مکان خصوصیت  "position"در برنامه p
vertexLoc = glGetAttribLocation(p,"position");
 
// مقید سازی بافر برای موقعیت ها و کپی کردن داده ها درون بافر
// GL_ARRAY_BUFFER نوع بافری است که ما برای تغذیه استفاده کرده ایم  
glBindBuffer(GL_ARRAY_BUFFER, buffer);
 
// تغذیه بافر , و اجازه دانستن به اپن جی ال که ما قصد تغییر آنرا نداریم (STATIC) و آن برای ترسیم استفاده شود (DRAW) 
glBufferData(GL_ARRAY_BUFFER, sizeof(pos), pos, GL_STATIC_DRAW);
 
// فعال کردن خصوصیات در آن مکان
glEnableVertexAttribArray(vertexLoc );
 
// به اپن جی ال بگوییم از آرایه ها شامل چیست :
// برای هر رأس شامل یک مجموعه 4 تایی از اعداد ممیز شناور float
glVertexAttribPointer(vertexLoc , 4, GL_FLOAT, 0, 0, 0);  



برای آرایه های دیگر ما فرآیندی شبیه همین را انجام می دهیم , تنها تفاوت در این است که normal دارای یک مجموعه با 3 عدد ممیز شناور برای هر رأس است , و  textureCoord فقط دوتاست . 
برای شاخص آرایه  رهیافت ساده تر است , تفاوت اصلی در نوع بافر است :
GLuint buffer;
glGenBuffers(1, &buffer);
 
// bind buffer for positions and copy data into buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), index, GL_STATIC_DRAW);

در تابع رندر , ما از اپن جی ال در خوسات ترسیم تعریف شده هندسی بالا را می کنیم . برای دستیابی به این ما می توانیم از کد زیر استفاده کنیم , برای VAO ها شامل یک شاخص بافر :
glUseProgram(p);
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, index.size(), GL_UNSIGNED_INT, NULL); 

یا , در نظر گرفتن VAO ها بدون رئوس :
glUseProgram(p);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, count);

----------


## amin1softco

*متغیر های Uniform* 
متغیر های Uniform همانند ثابت ها عمل می کنند . حداقل برای در زمان فراخوانی ترسیم .  برنامه این متغیر ها را به خط تولید گرافیکی تغذیه میکند و آنها در تمام مراحل خط تولید قابل دسترسی است , برای مثال هر سایه زن می تواند به هر متغیر Uniform دسترسی داشته باشد , تا زمانیکه , آن متغیر ها را تعریف کند . این متغیر ها فقط خواندنی هستند , تا آنجا که سایه زن ها علاقمندند . 
Uniform ها می تواند در بلاک ها گروهبندی شوند , یا منحصراً تعریف شده , در اینجا ما شروع به نگاه به تعریف ها انحصاری می کنیم , و در قسمت بعدی به بلاک های Uniform  نگاه می کنیم .
داخل یک سایه زن یک Uniform با واژه کلیدی uniform تعریف می شود . برای مثال برای تعریف یک vec4 به نام myVar ما می توانیم انرا داخل یک سایه زن بنویسیم .

uniform vec4 myVar;

Uniform ها می توانند همچنین داخل سایه زن مقدار دهی اولیه شود , برای نمونه ما می توانیم فرآیند زیر را برای مقدار دهی اولیه به  vec4 استفاده کنیم :
uniform vec4 myVar = {0.5, 0.2, 0.7, 1.0};

در-مقدار دهی اولیه - سایه زن - از آن نقش عالی ما را نیاز به تعیین uniform ها می کند بهترین موقع است ! ما می توانیم یک پیش فرض مشخص در مقدار دهی اولیه سایه زن کنیم , فقط مجبوریم یک مقدار از برنامه مشخص کنیم اگر ما به یک مقدار متفاوت برای متغیر uniform  نیاز داریم . 

داخل برنامه , برای تعیین مقدار  یک متغیر  uniform ما باید اول موقعیت را بگیریم , که ما می توانیم دریافت کنیم با تابع زیر :
GLint glGetUniformLocation(GLuint program, const char *name);

پارامترها:

    program:هندل به یک برنامه پیوند خورده
    name: نام متغیر uniform 

بازگشتی: مکان متغیر , یا -1 اگر نام مطابق متغیر uniform نباشد. 

یک متغیر uniform متغیری است که در واقع درون سایه زن استفاده شده , فقط تعریف نشده . کامپایلر در دور انداختن متغیر های که استفاده نمی شوند در کد آزاد است . بنابراین , حتی اگر یک uniform در سایه زن تعریف شده , تا زمانیکه بکار برده نشود , مکان گزارش شده می تواند -1 باشد . 

مقدار بازگشتی , فرض می کند برنامه پیوند خورده است و متغیر های آن بطور موثر در کد  استفاده شده است , که مکان متغیر است , که بعداً می تواندبرای تعیین مقادیر استفاده شود .   به منظور تعیین مقادیر , OpenGL یک خانواده بزرگ از توابع را پیشنهاد می کند . برای پوشش تمام نوع های داده ایی , و چندین راه برای نشاندن مقادیر . برای نمونه , متغیر myVar را همانطور که دربالا تعریف شد در نظر بگیرید .برای تعیین  vec4 , و فرض p به عنوان هندل به برنامه پیوند خورده , ما می توانیم بنویسیم :
GLint myLoc = glGetUniformLocation(p, "myVar");
glProgramUniform4f(p, myLoc, 1.0f, 2.0f, 2.5f, 2.7f);

یا , به صورت زیر :
float myFloats[4] = {1.0f, 2.0f, 2.5f, 2.7f};
GLint myLoc = glGetUniformLocation(p, "myVar");
glProgramUniform4fv(p, myLoc, 1, myFloats);

امضاء این دو تابع به صورت زیر است :
void glProgramUniform4f(GLuint program, GLint location, GLfloat f1, …, GLfloat f4);
void glProgramUniform4fv(GLuint program, GLint location, GLsizei count, const GLfloat *values);

پارامتر ها:

    program: هندل به برنامه پیوندی
    location: مکان uniform’s در برنامه p
    f1…f4, values: مقادیر برای تعیین uniform
    count: تعداد اقلام که باید مقدار دهی شود. برای نوع داده های پایه این همیشه یک است , زمانیکه آرایه ها را در نظر بگیریم , سپس این مقدار تعداد عناصر در آرایه که باید مقدرا دهی شود را مشخص میکند .

توابع مشابهی برای تمام نوع های پایه در GLSL است و برای ماتریس های از پیش تعریف شده .

*آرایه ها* 

در نظر بگیرید حالا در سایه زن ما آرایه ایی از نوع متغیر ها که به صورت زیر تعریف شده :
uniform vec4 color[2];

نقطه نظربرنامه , ما می توانیم به متغیر ها همانند یک آرایه نگاه کنیم و تمام اجزاء را یکجا مقدار دهی کنیم. این برای اکثر نوع های پایه صحیح است , غیر از ساختارها , پایین را ببنید :
float myColors[8] = {1.0f, 0.0f, 0.0f, 0.0f,     0.0f, 1.0f, 0.0f, 0.0f};
GLint myLoc = glGetUniformLocation(p, "color");
glProgramUniform4fv(p, myLoc, 2, myColors);

توجه کنید ما سومین پارامتر از glProgramUniform4fv را 2 مقدار دهی کردیم , زمانیکه ما دو عنصراز آرایه را نشاندیم , برای مثال دو vec4 . آرایه برنامه myColors به هشت ممیز شناور نیاز دارد . 

رهیافت دیگر می تواند این باشد هر عنصر را بطور مجزا مقدار دهی کنیم. برای نمونه , فرض کنید ما فقط می خواهیم عنصر دوم از آرایه GLSL را مقدار دهی کنیم . برای مثال : color[1] سپس می توانیم بنویسیم :
GLfloat aColor[4] = {0.0f, 1.0f, 1.0f, 0.0f};
myLoc = glGetUniformLocation(p, "color[1]");
glProgramUniform4fv(p, myLoc, 1, aColor);

اینجا یک تابع  glProgramUniform است برای هر نوع داده پایه و آنها آمدند تا در دو نوع برای مورد vec4 نشان دهد :
void glProgramUniform4f(GLuint program, GLint location, GLfloat v1, …, GLfloat v4);
void glProgramUniform4fv(GLuint program, GLint location, GLsizei count, GLfloat *v);

پارامترها:

    program:هندل برنامه پیوندی
    location: مکان متغیر
    v1..v4: مقدار هر جزء از  vec4
    count: تعداد از vec4 که باید تعیین شود.
    v:اشاره به جاییکه داده های ممیز شناور پیدا شود .

برای ماتریس ها , یک نسخه خاص از این تابع است , مثال زیر را ببینید برای یک mat4:

void glProgramUniformMatrix4fv(GLuint program, GLint location, GLsizei count, GLboolean transpose, GLfloat *v);

پارامتر ها:


    program:هندل برنامه پیوندی
    location: مکان متغیر
    count: تعداد از vec4 که باید تعیین شود.
     transpose: اگر true باشد ماتریس ترانهاده می شود قبل از تبدیل به uniform 
    v:اشاره به جاییکه داده های ممیز شناور پیدا شود .


*ساختار ها :*
در GLSL ما می توانیم ساختار ها را تعریف کنیم , به روش شبیه به C . برای نمونه , قطعه کد تعریف شده زیر یک ساختار با دو vec4 , و تعاریف یک متغیر uniform از همان نوع :
struct Colors{
    vec4 Color1;
    vec4 Color2;
};
 
uniform Colors myColors;

برای استفاده فیلد های ساختارها درون یک سایه زن ما از همان شیوه C استفاده می کنیم . برای نمونه , تابع main زیر با فرض تعریف بالا ست :
void main()
{
    vec 4 aColor = myColors.Color1 + myColors.Color2;
    ...
}

برای مقدار دهی این متغیر ها در برنامه ما دوباره از شیوه C ساتفاده می کنیم . ما نمی توانیم کل ساختار را مقدار دهی کنیم , همانطور که نمی توانیم یک مکان برای آن بگیریم . ما باید آنرا فیلد به فیلد مشخص کنیم . مثال زیر چگونگی کار ار نشان می دهد :
float myFloats[8] = {0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
 
GLint myLoc = glGetUniformLocation(p, "myColors.Color1");
glProgramUniform4fv(p, myLoc, 1, myFloats);
myLoc = glGetUniformLocation(p, "myColors.Color2");
glProgramUniform4fv(p, myLoc, 1, &(myFloats[4]));

کار با آرایه هایی از ساختار ها خیلی ساده است . تعریف uniform زیر را در نظر بگیرید :
uniform Colors myColors[2];

در سایه زن ما می توانیم بصورت زیر بنویسیم :
void main()
{
    vec 4 aColor = myColors[0].Color1 + myColors[1].Color2;
    ...
}

داخل برنامه ما همان شیوه را دنبال می کنیم :
GLint myLoc;
float myFloats[8] = {0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
float myOtherFloats[8[ = {1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0};
 
myLoc = glGetUniformLocation(p, "myColors[0].Color1");
glProgramUniform4fv(p, myLoc, 1, myFloats);
myLoc = glGetUniformLocation(p, "myColors[0].Color2");
glProgramUniform4fv(p, myLoc, 1, &(myFloats[4]));
myLoc = glGetUniformLocation(p, "myColors[1].Color1");
glProgramUniform4fv(p, myLoc, 1, myOtherFloats);
myLoc = glGetUniformLocation(p, "myColors[1].Color2");
glProgramUniform4fv(p, myLoc, 1, &(myOtherFloats[4]));

----------


## amin1softco

بلاک های Uniform به دو دلیل ویژگی مناسبی است  :

    اجازه اشتراک گذاری uniform ها بین برنامه - یکبار مقدار دهی , چندین باراستفاده 
    اجازه نشاندن یکباره چندین مقدار

سینتکس GLSL کاملاً سر راست است . برای نمونه برای تعریف یک بلاک با دو رنگ در یک سایه زن ما می توانیم چیزی شبیه کد زیر را بنویسیم :
uniform ColorBlock {
    vec4 diffuse;
    vec4 ambient;
};

کد بالا یک بلاک به نام ColorBlock, تعریف می کند با دو متغیر vec4 درون آن . در مشخصات (مستندات) این بلوک ها به عنوان named blocks مشخص شده اند و تمام uniform ها خارج از این بلوک های نام متعلق به یک بلوک پیش فرض هستند .
درون توابع سایه زن ها ما فقط با دو uniform کار می کنیم , بدون نیازی به استفاده از بلوک های نام . 

...
out vec4 outputF;
 
void main() {
    outputF = diffuse + ambient;
}

مخزن پیش فرض برای یک بلاک بستگی به پیاده سازی دارد . هرچند , گزینه ها دیگر موجود هستند , و ما می توانیم مشخص کنیم یک حالت مخزن برای بلاک با یک توصیفگر  layout . گزینه های موجود عبارتند از :

    std140: بسته بندی متغیر ها به دنبال قوانین تعریف شده در مشخصات (مستندات)OpenGL است . بلاک ها با این layout می توانند بین سایه زن ها به اشتراک گذاشته شوند .
    shared: مخزن ها به پیاده سازی بستگی دارند , اما کامپایلر اطمیان خواهد داد که که بلاک هنوز  بین سایه زن های مختلف  قابل اشتراک گذاری است .
    packed: کامپایلر مخازن بلاک ها را بهینه خواهد کرد , بالقوه تمام متغیر های که استفاده نمی شوند در سایه زن حذف می کند . این نوع بلاک ها نباید به شاتراک گذاشته شوند .

مزیت عمده استفاده از  std140 , قوانین مخزن به apriori شناخته می شوند . که می تواند به ما در نظاندن کامل بلاک کمک کند , اما ما بعداً به این موضوع می پردازیم. 

* بر پا سازی OpenGL*
بلاک ها در سایه زن ها از طریق نقطه مقید سازی به بافر های OpenGL متصل هستند . ایده خیلی ساده است , هر بلاک یک شاخص است , که می تواند به نقطه مقیدسازی مقید شود . بافر ها همچنین دارای یک Id هستند , که می تواند به همان نقطه مقید شوند , از این رو برقرار کردن یک ارتباط بین داده بافر ها ذخیره و بلاک سایه زن ها نیاز است . 
در شکل زیر , بلاک A1 و B1 نقاط مقید سازی یکسان دارند , از این رو داده های یکسانی را به شاتراک می گذارند . برای دو بلاک که بافر های یکسانی را به اشتراک گذاشته اند آنها باید  layout یکسانی داشته و باید تعریف داده های یکسان داشته باشند . 


زمانیکه یک سایه زن پیوند شده (در بعضی موارد داشتن  یک فرمان پیوند صادر شده ممکن است کافی باشد , به مستندات نگاه کنید) هر بلاک دارای یک شاخص انتصابی است . برای گرفتن شاخص بلاک ها ما می توانیم از تابع lGetUniformBlockIndex استفاده کنیم .

GLuint glGetUniformBlockIndex(GLuint program, const GLchar * uniformBlockName);

پارامتر ها:

    program: هندل به برنامه
    uniformBlockName: نام بلاک  uniform

بازگشتی: شاخص بلاک, یا INVALID_INDEX اگر نام به بلاک فعال مربوط نباشد.

همانطور که قبلاً ذکر کردیم , بلاک ها مربوط به بافر ها می شوند از طریق نقطه مقید سازی , هر دو , بافر و بلاک , باید یک نقطه مقید سازی یکسان معین شود . برای مشخص کردن یک بلاک به یک نقطه مقید ساز ی ما از تابع زیر استفاده می کنیم :
void glUniformBlockBinding(GLuint program, GLuint uBlockIndex, GLuint uBlockBinding);

پارامتر ها:

    program: هندل به برنامه پیوندی 
    uBlockIndex: شاخص بلاک uniform
    uBlockBinding: نقطه مقید سازی 

ما همچنین نیاز به ساخت یک بافر , در این مورد یک GL_UNIFORM_BUFFER , ایجاد ذخیره داده آن , و مشخص کردن آن به نقطه مقید سازی همانند بلاک . برای ایجاد بافرهای دخیره داده ما می توانیم از glBufferData استفاده کنیم . 
آخرین مرحله  می تواند با glBindBufferBase تکمیل شود .
void glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);

پارامتر ها:

    target: در این محتوی باید GL_UNIFORM_BUFFER باشد.
    size: اندازه ذخیره داده
    data: یک اشاره گر به داده , یا NULL
    usage: یک اشاره . داده باید برای خواند فعال باشد , اما اغلب توانایی تغییر داریم , بنابراین GL_DYNAMIC_DRAW یک گزینه خوب است .

void glBindBufferBase(GLenum target, GLuint bindingPoint, GLuint bufferName);

پارامتر ها:

    target:در انی محتوی باید GL_UNIFORM_BUFFER باشد .
    bindingPoint: نقطه مقید سازی
    bufferName: نام بافر

قطعه کد زیر همه را با هم قرار می دهد . آن فرض کرده که p یک برنامه GLSL است که یک فرمان پیوند صادر شده است .
// the binding point must be smaller than GL_MAX_UNIFORM_BUFFER_BINDINGS
GLuint bindingPoint = 1, buffer, blockIndex;
float myFloats[8] = {1.0, 0.0, 0.0, 1.0,   0.4, 0.0, 0.0, 1.0};
 
blockIndex = glGetUniformBlockIndex(p, "ColorBlock");
glUniformBlockBinding(p, blockIndex, bindingPoint);
 
glGenBuffers(1, &buffer);
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
 
glBufferData(GL_UNIFORM_BUFFER, sizeof(myFloats), myFloats, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, buffer);


بعد از این مرحله ها , بافر به بلاک پیوند خورده . برای تغذیه مقادیر به بلاک سایه زن ها , آنچه نیاز داریم کپی کردن داده به بافر های دخیره داده است .

نشاندن و بروز کردن بافر
بگذارید بلاک زیر را که در سایه زن تعریف شده در نظر بگیریم :
layout (std140) uniform ColorBlock {
    vec4 diffuse;
    vec4 ambient;
};
برای تعیین متغیر های مجزا ما نیاز به گرفتن افست نسبت به نقطه شروع از بافر داریم . در واقع گرفتن این اطلاعات کمی سخت است . ما نیاز داریم به دو تابع برای فهمیدن محتویات درون بلاک داریم , فرض کنید ما شاخص بلاک ها را داریم , که , قبلاً دیدیم , که با تابع glGetUniformBlockIndex قابل دریافت است .

void glGetActiveUniformBlockiv(GLuint program, GLuint uBlockIndex, GLenum pname, GLint *params);

پارامتر ها:

    program: هندل به برنامه
    uBlockIndex:شاخص بلوک ها
    pname: چیزی که می خواهیم بگیریم
    result: جاییکه داده ها بازگشت می شود.

در مورد پارامتر pname ما ممکن است از  GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS برای گرفتن تعداد uniform ها در بلاک , و سپس GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES برای یافتن رئوس از آن uniform ها استفاده کنیم .
زمانیکه ما رئوس را داریم می توانیم از glGetActiveUniformsiv برای گرفتن داد ه uniform ها استفاده کنیم .
void glGetActiveUniformsiv(GLuint program, GLsizei ucount, const GLuint *uIndices, GLenum pname, GLint *params);

پارامترها:

    program: هندل به برنامه
    ucount: تعداد رئوس
    uindices: لیست رئوس
    pname: اطلاعاتی که می خواهیم دریافت کنیم
    result: جاییکه اطلاعات بازگشت می شود .

در مورد پارامتر pname ما می توانیم از فیلد های زیر به ما در مشخص کردن مکان uniform خاص که در بافر داده ذخیره شده کمک می کند , و چگونه حافظه آنها بسته بندی شده است :
GL_UNIFORM_TYPE, GL_UNIFORM_OFFSET, GL_UNIFORM_SIZE, GL_UNIFORM_ARRAY_STRIDE, GL_UNIFORM_MATRIX_STRIDE.

بگذارید به یک مثال خاص نگاه کنیم . بلاک بالا را در نظر بگیرید , به نام ColorBlock با دو vec4 . ما می توانیم ازکتابخانه VSGLInfoLib برای بدست اوردن اطلاعات در مورد بلاک uniform استفاده کنیم . برای بلاک بالا ما می توانیم دریافت کنیم:
ColorBlock
    Size 32
    Block binding point: 0
    Buffer bound to binding point: 0 
    {
       ambient
            GL_FLOAT_VEC4
            offset: 16
            size: 16
       diffuse
            GL_FLOAT_VEC4
            offset: 0
            size: 16
    }

همانطور که دیدم هر متغیر 16 بایت اندازه می گیرد , که انتظار می رود از هر ممیز شناور 4 بایت باشد و هر vec4 4 ممیز شناور . ما می توانیم همچنین ببینیم , در این مورد , افست ها با اندازه ها تطابق دارد , برای مثال درمین uniform از جایی شروع می شود که اولی تمام شده .

برای تعیین مقادیر uniform ما مجبوریم بافر های دخیره داده را پر کنیم . یک تابع که می تواند در این مورد استفاده شود عبارت است از :
void glBufferSubData( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data);

پارامتر ها:

    target: GL_UNIFORM_BUFFER مقداری که ما در این محتوی می خواهیم 
    offset: افست در  bytes به اولین نقطه دخیره داده بستگی دارد .
    size: مقدار بایت ها که ما می خواهیم کپی کنیم در دخیره داده 
    data: داده هایی که ما می خواهیم در ذخیره داده کپی کنیم 

بنابرانی ما می توانیمuniform ,  ambient را به صورت زیر مشخص کنیم:


float color[4] = {0.3, 0.0, 0.0, 1.0};
GLuint offset = 16;
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
glBufferSubData(GL_UNIFORM_BUFFER, offset , sizeof(color), color);

برای پر کردن کامل بافر ما می توانیم هوز از همان تابع استفاده کنیم . برای نمونه ما می توانیم از کد زیر برای مشخص کردن مقادیر برای هردو diffuse و ambient استفاده کنیم .
float color[8] = {0.8, 0.0, 0.0, 1.0,     0.3, 0.0, 0.0, 1.0};
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(color), color);

حالا , اجازه دهید یک بلاک با کمی تفاوت را درنظر بگیریم :
layout (std140) uniform ColorBlock2 {
    vec3 diffuse;
    vec3 ambient;
};

بکار بردن VSGLInfoLib برای دریافت اطلاعات باک ها که ما گرفتیم :
ColorBlock2
    Size 28
    Block binding point: 0
    Buffer bound to binding point: 0 
    {
       ambient
            GL_FLOAT_VEC3
            offset: 16
            size: 12
       diffuse
            GL_FLOAT_VEC3
            offset: 0
            size: 12
    }

همانطور که در توضیحات بلاک بالا می بینیم , افست برای ambient شانزده بات است , اگرچه اندازه diffuse , اولین uniform در بلاک , فقط 12 بایت است . این به دلیل این است که با layout std140 بvec3 ها با  vec4 هم تراز می شوند . مجموعه کامل نتایج در مستندات موجود می باشد .  

بنابراین , به مثال خاص خود بر می گردیم از ColorBlocks2 , برای پر کردن کامل بافر های دخیره داده , ما نیاز به در نظر گرفتن نه تنها اندازه ها  همچنین نیاز به افست ها داریم . برای مقدار دهی بافر ما نیاز به ساخت یک آرایه با 28 بایت داریم, اندازه بلاک , یا 7 ممیز شناور . برای گرفتن رنگ diffuse به  (0.8, 0.2, 0.2) و ambient به (0.4, 0.1, 0.1) , سه ممیز شناور اول رنگ diffuse خواهد بود . یک ممیز شناور برای اهداف هم تراز سازی برای گرفتن افست صحیح برای دومین متغیر , و 3 ممیز شناور دیگر برای قسمت ambient.
GLuint bindingPoint = 1, buffer, blockIndex;
float myFloats[7] = {0.8, 0.2, 0.2,     0.0,   0.4, 0.1, 0.1};
 
glGenBuffers(1, &buffer);
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
 
glBufferData(GL_UNIFORM_BUFFER, sizeof(myFloats), myFloats, GL_DYNAMIC_DRAW);


همین توجه زمانیکه از ماتریس ها و آرایه ها استفاده می کنیم باید اعمال شود. برای نمونه یک ماتریس ممیز شناور 3*3 نیاز به 4 ممیز شناور برای هر ستون برای اهداف همتراز سازی استفاده شود . این اطلاعات , گام ماتریس , می توان با glGetActiveUniformsiv گرفته شود با پارامتر GL_UNIFORM_MATRIX_STRIDE . برای آرایه ها که پارامتر GL_UNIFORM_ARRAY_STRIDE است . مستندات را بخوانید برای درک کامل اصول پشت طرح حافظه , و استفاده از VSGLInfoLib تأیید که شما واقعاً مطلب را گرفته اید.

----------


## amin1softco

*ورود به ارتباطات سایه زن*
سایه زن ها بین مراحل خط تولید در کیان متغیر ها ارتباط بر قرار می کند . برای نمونه یک سایه زن رأس برای هر رأس در خروجی یک رنگ دارد . از جمله سایه زن ها یی که خروجی رأس دارند (رأس , هندسی و مفروش سازی رئوس) , خروجی یک مرحله به ورودی مرحله بعد کپی می شود .

سپس این مقادیر به اسمبل (طرح) اولیه و مرحله تصویر گری تغذیه می شوند , که مقدار رنگ جاسازی می شود برای هر قطعه حاصل (نتیجه) . سرانجام قطعات به سایه زن قطعه تغذیه می شود . بنابراین , ورودی های سایه زن قطعه مقادیر جاسازی شده(درون یابی شده) هستند .

برای تعیین یک متغیر به عنوان ورودی یا خروجی به سا یه زن داده شده واژه های کلیدی in و out موجود هستند .   ما این واژه های کلیدی را  که در حال استفاده برای ورودی ها ی سایه زن رأس و خروجی سایه زن قطعه موجود هستند می بینم .
هر چند این قسمت , تمرکزش فقط روی ورود به ارتباطات سایه زن است .
GLSL چندین مکانیسم برای تطبیق خروجی های یک مرحله به ورودی مرحله بعدی تدارک دیده است . جزئیات هر کدام از این ها در زیر قسمت بعدی تشریح خواهد شد . , کاوش pros و cons برای هر مکانیسم .

*تطبیق بر پایه نام*

ساده ترین مکانیسم های تطبیق , از نقطه نظر سینتکس , برپایه نام است . برای نمونه ما می توانیم نام یک متغیر خروجی را در سایه زن رأس color در نظر بگیریم , و یک ورودی داشته باشیم با همان نام در سایه زن قطعه .
// vertex shader
out vec4 color;
 
-------------------
// fragment shader
in vec4 color;

تطبیق بر پایه نام از نظر سینتکس ساده است و اگر فقط دو مرحله سایه زنی در نظر گرفته شود , می تواند یک راه حل عالی باشد . مشکل راه حل بالا زمانی خود نمایی می کند که ما مرحله سوم را اضافه می کنیم . برای نمونه یک سایه زن هندسی , به خط تولید تصویر نهایی .

همانطور که خروجی سایه زن رأس color نامیده می شود , ورودی سایه زن هندسی با این نام مطابقت داشته باشد . هرچند , ما باید حالا نام دیگری برای خروجی سایه زن هندسی  پیدا کنیم  , و در نتیجه تغییر نام ورودی و نتیجتاً تغییر کد سایه زن قطعه . توزیع متغیر جدید بین سایه زن ها می تواند به شکل زیر صورت گیرد :
// vertex shader
out vec4 color;
 
-------------------
// geometry shader
in vec4 color[];
out vec4 colorFromGeom;
 
---------------------
// fragment shader
in vec4 colorFromGeom;

همانطور که از کد بالا دیدیم , سایه زن قطعه باید تغییر کند تا نام ورودی جدید را منعکس کند برای متغیری که شامل رنگ جاسازی شده قطعات باشد .
اما اگر ما با استفاده از همان سایه زن رأس و قطعه در دو خط تولید : یکی با سایه زن هندسی , و دیگری بدون آن ؟ تنها راه حل زمانی است که دو نسخه از سایه زن قطعه با نام های ورودی متفاوت داشته باشیم .

و حالا , اگر ما بخواهیم کمی تغییر در سایه زن قطعه ایجاد کنیم   , ما دو سایه زن برای تغییر داریم ...
کاملاً واضح است که چنین چیزی بسختی دست یافتنی است و حفظ و دیباگ (رفع خطا) با این رهیافت مشکل است .


*تطبیق بر پایه مکان* 
هر متغیر دارای یک مکان است , و از این می توان برای تطابق ورودی از یک مرحله خط تولید به مرحله بعدی استفاده کرد . دو متغیر با هم مطابق هستند زمانیکه مکان خروجی از یک مرحله با مکان ورودی مرحله بعدی مطابق باشد . بکار بردن تطابق بر پایه مکان , نام متغیر ها نیازی به همزمان بودن ندارد , برای نمونه توزیع متغیر زیر را در یک سایه زن رأس و قطعه را در نظر بگیرید :
// vertex shader
layout (location = 0) out vec3 normalOut;
layout (location = 1) out vec4 colorOut;
 
---------------------
// fragment shader
layout (location = 0) in vec3 normalIn;
layout (location = 1) in vec4 colorIn;

در این مورد  normalOut از سایه زن رأس مطابق normalIn از سایه زن قطعه خواهد بود , زمانیکه آنها یک مکان را به اشتراک می گذارند . به همین استدلال برای colorOut وcolorIn بکار می رود.

حالا چه اتفاقی می افتد اگر ما یک سایه زن هندسی به خط تولید اضافه کنیم ؟ بر خلاف تطابق بر پایه نام , این هیچ پیامد هایی روی سایه زن قطعه تا زمانیکه ما را به حساب مکان قبلی است !!!!!
// vertex shader
layout (location = 0) out vec3 normalOut;
layout (location = 1) out vec4 colorOut;
 
---------------------
// geometry shader
layout (location = 0) in vec3 normalIn[];
layout (location = 1) in vec4 colorIn[];
 
layout (location = 0) out vec3 normalOut;
layout (location = 1) out vec4 colorOut;
 
---------------------
// fragment shader
layout (location = 0) in vec3 normalIn;
layout (location = 1) in vec4 colorIn;
متغیر های خروجی از سایه زن رأس با متغیر های ورودی از سایه زن هندسی وفق داده می شوند , و متغیر های خروجی از سایه زن هندسی با متغیر های ورودی سایه زن قطعه وفق داده خواهند شد . همانطور که در مثال بالا دیدیم , نیازی به آمدن نام های جدید برای ورودی های سایه زن قطعه  نیست , از این رو بر خلاف تطبیق بر پایه نام آنها با این روش از بین رفتند.

هر چند , اینجا موضوعاتی را در نظر بگیریم با تطبیق بر پایه مکان . اول ما باید یک متخصص در مشخص کردن مکان ها داشته باشیم . برای نمونه, کد زیر خطای کامپایل تولید می کند :
// vertex shader
layout (location = 0) out vec3 someAttribute[2];
layout (location = 1) out vec4 colorOut;
 
main() {
    someAttribute[1] = …;
    colorOut = …;
    …
ب NVIDIA drivers 305.67 خطای زیر در یافت می شود :
error C5121: multiple bindings to output semantic "ATTR1"

هر مکان , یک مکان بردار است , برای مثال می تواند یک بردار 4 عنصری از ممیز شناور یا عدد صحیح را نگاه دارد . از این رو , دومین عنصر آرایه از someAttribute مکان مشابهی با colorOut را به اشتراک می گذارد . کامپایلر واقعاً بطور کامل مجاز است , و تنها خروجی یک خطا است زمانیکه به مکان با هر دو متغیر در سایه زن های یکسان دسترسی پیدا شود .

تطبیق بر پایه نام  بنابراین نیاز به توجه بیشتری دار د. بردار های دوبل dvec3 و dvec4 نیاز به 2 مکان دارند . ماتریس ها همچنین چندین مکان را می گیرند , یکی برای هر خط ماتریس برای ممیز های شناور و اعداد صحیح . اشاره گر ساختار ها مکان ها را  برای هر فیلد  می شمارد.

برای نمونه ساختار زیر را در نظر بگیرید :
layout(location = 0) out struct S{
    vec3 normalOut;
    mat3 aMatrix;
    int a;
    float b;
}s;

متغیر normalOut مکان 0 را خواهد گرفت , aMatrix دارای مکان های 1,2و3 , یکی برای هر خط . مکان 4 در a ذخیره می شود و مکان 5 در b .

بنابراین اگر ما قصد تعریف خروجی دیگر بر اساس رهیافت تطبیق بر پایه نام را داشته باشیم ما مجبوریم از مکان 6 استفاه کنیم . روی این سایه زن , و تمام سایه زن های دیگر که به عنوان ورودی , خروجی های این سایه زن خاص هستند . دوباره همه چیز برای گرفتن از دست داده شروع شد ...

نتیجتاً , بر پایه مکان درای مزیتی  نسبت به تطبیق بر پایه نام است  . چرا که این دارای پیاده سازی روی متغیر های نام نیست , از این رو کدی برای نوشتن مجدد نیست . از طرف دیگر شمارش مکان ها اصلاً خوشایند نیست . علاوه بر این , تغییر یک نوع داده خروجی , یا اعضای ساختار , باعث تغییر اشاره گر در تمام مکان های بعدی می شود . 


*تطبیق بر پایه بلوک* 
رهیافت سوم بر اساس بلوک های واسطه است . ما در حال حاضر بلوک ها را در بخش قبلی پوشش دادیم . ورود ارتباطات سایه زن بلاک ها شبیه به بلاک های uniform در ساخت و ساز خود هستند . بلاک ها می توانند دارای چندین فیلد باشند , و تطبیق با نان بلاک انجام می شود .

یک مثال از بلاک :
out Data {
    vec3 normal;
    vec3 eye;
    vec3 lightDir;
    vec2 texCoord;
} DataOut;

در مثال بالا , Data در نام بلاک ها , و  DataOut یک نام برای مثال است . درون سایه زن ما به متغیر های درون بلاک ارجاع میک نیم , پیشوند آنها با نام مثال . برای نمونه :
DataOut.normal = normalize(someVector);

یا این رهیافت ما می توانیم بلاک هایمان را در سایه زن قطعه و رأس به صورت زیر تعریف کنیم :
// vertex shader
out Data {
    vec3 normal;
    vec3 eye;
    vec3 lightDir;
    vec2 texCoord;
} DataOut;
 
----------------------------
// fragment shader
in Data {
    vec3 normal;
    vec3 eye;
    vec3 lightDir;
    vec2 texCoord;
} DataIn;

توجه کنید که تطبیق بوسیله نام بلاک ها انجام می شود , Data , نه بوسیله نام مثال , DataOut در سایه زن رأس و DataIn در سایه زن رأس .

برای اضافه کردن یک سایه زن هندسی به خط تولید ما فقط نیاز به تعریف یک بلاک ورودی که با خروجی تطبیق شود داریم , و یک خروجی با ورودی سایه زن قطعه تطبیق می شود . از این رو , در سایه زن همندسی مان می توانیم چیزی مثل این داشته باشیم :
in Data {
    vec3 normal;
    vec3 eye;
    vec3 lightDir;
    vec2 texCoord;
} DataIn[];
 
 
out Data {
    vec3 normal;
    vec3 eye;
    vec3 lightDir;
    vec2 texCoord;
} DataOut;

توجه کنید آن بلاک ها با همان نام باید داری عضوهای یکسان باشد , یا حداقل دارای کاربرد یسکان حافظه باشند .

همانطور که در تطبیق بر پایه مکان , کاربرد بلاک ها اجازه اضافه / حذف بلاک ها از مراحل خط تولید تصویر بدون تأثیر کد مراحل باقی مانده به ما می دهد . علاوه بر این , تطبیق بر پایه بلاک دارای مزیت کمترین استعداد خطا است که خیلی ساده تر از رهیافت تطبیق بر پایه مکان است . تا زمانیکه ما از تعریف بلاک ها با عضو های دقیقاً یکسان حفاظت کنیم ما در مسیر خوبی هستیم.

----------


## amin1softco

قبل از اینکه ما شروع به نمایش تعدادی از سایه زن ها کنیم , ما کمی در مورد فضا یا ارجاعی ها و ماتریس ها  صحبت خواهیم کرد . این ها مفاهیم اساسی زمانیکه ما معمولاً در بیشتر فضاها عمل می کنیم در خط تولید تصویر است , و برقرار کردن یک زمینه مشترک مهم است برای درک اتفاقی که درون سایه زن در واقع در حال انجام است.

4 فضای مرتبط در CG برای هدف ما :

*local space*: فضایی که در آن مدل ها ساخته شده.
*world space*: زمانیکه ما صحنه 3 بعدی را اسمبل می کنیم .
*camera space*: فضایی که در ان دوربین در مبدأ است , و به پایین نگاه می کند روی محور منفی Z.
*clip space*: فصای پست پروجکشن post projection  , وقتی که دید frustum (جزء - هرم کوچک)تبدیل به یک مکعب می شود , به مرکزیت مبدأ مختصات , که از -1 تا 1 در هر محور ادامه دارد .

برای تبدیل بین این 4 فضا ما 3 ماتریس 4*4 داریم :

*model matrix*: برای تبدیل از local به world space;
*view matrix*: برای تبدیل از world space به camera space.
*projection matrix*:برای تبدیل از camera space به clip space.
برای راحتی ما همچنین قصد در نظر گرفتن دو ماتریس مرکب را داریم :


ماتریس های بالا فقط میانبر هایی هستند که به ما اجازه حرکت سریع تر بین فضا ها را می دهند , باعث صرفه جویی در جمع و ضرب ها لازم برای حرکت از فضا ها که روز نمودار مجاور نیستند می شود .
هر دو ماتریس  model و view بر پایه جابه جایی , چرخش ها , تغییر اندازه ها است . این تبدیلات هندسی نمایشگر تغییر شکل معین هستند برای مثال یک تبدیل که یک خط راست و قسمت ها را نگه می دارد - اگر این نقاط در خط راست باشند , آنها در خط راست باقی می مانند بعد از انجام تبدیل و تبدیل نقطه میانی از خط هنوز نقطه میانی خط تبدیل شده است . . علاوه بر این , زمانیکه لازم به نگه داری زاویه ها یا طول ها نیست, دو خط موازی بعد از تبدیل هنوز موازی هستند .

شکل ماتریس برای یک تبدیل معین عمومی , از مختصات هموژنیزه (مشابه) استفاده می کند , که یک ماتریس که آخرین ردیفش [0 0 0 1] است تشکیل شده . آخرین ستون شامل تبدیل است و زیر ماتریس های 3*3 بالایی برای چرخش و تغییر اندازه هستند . 
در اینجا , ما فقط قصد در نظر گرفتن تبدیلات معین با درنظر گرفتن تبدیلات بین اولین 3 فضا را داریم . 

ماتریس پروجکشن معمولاً نمایش گر یک پروجکشن پرسپکتیو است که در کلاس های یکسان از تبدیلات نیستند, همانند خطوط موازی نیاز نیست بعد از پروجکشن موازی باشند . از این رو پرسپکتیو پروجکشن دارای تبدیلات معین نیست .

*تبدیلات بین فضاها (محلی , جهان, دوربین) ->(local, world, camera)*
برای تبدیل یک نقطه , 4 عنصر چندتایی (x,y,z,1.0), بین فضاها ما از ماتریس بالا استفاده میک نیم . برای نمونه , برای تبدیل یک نقطه P از فضای local به camera ما می نویسیم :



زمانیکه ما فقط تبدیلات معینی را در نظر داریم ما می دانیم نقطه تبدیل شده دارای مقدار عمومی (x’, y’, z’, 1.0) است .

بردرا ها , 4 عنصر چندتایی از فرم عمومی (x, y, z, 0.0) , با procedure یکسان تبدیل شده اند , و بردار های تبدیل شده دارای فرم عمومی  است . بطور خاص , همانطور که 4 عنصر از یک بردار صفر هستند , ما یک نتیجه برابر با استفاده فقط سه جزء اول بردار  می گیریم و سه زیرماتریس 3*3 بالایی .
بالایی برای تمام بردارها استفاده می شود که می تواند بیانگر تفاوت بین دو نقطه باشد . چنین برداری در نظر بگیرید , سپس تبدیل بردار برابر تبدیل هر یک از نقاط است , و محاسبه تفاضل نقطه تبدیل شده .





بردار های نرمال یک مورد خاص هستند , که با این روش نمی توان تشریح کرد . یک بردار نرمال یک بردار است که داری مقدار (ارزش) ثابت است , 1.0 و یک جهت  که به عنوان تفاوت بین دو نقطه , اما یک جهت که عمود بر سطح است .
از این رو , برای تبدیل یک بردار نرمال ما باید از یک ماتریس استفاده کنیم که تبدیل بردار ها خصوصیات اعمود بودن به سطح را حفظ کند . بنابراین چگونه محاسباتمان را با چنین ماتریسی انجام دهیم ؟

در چپ مثلث اصلی است و یک بردار نرمال به بدنه آن . راست ما یک شکل که تمام نقاط و بردار ها ی آن تبدیل شده داریم با یک مقیاس (1,2,1). 

بردار T مماس به اطراف مثلث است و می تواند به صورت زیر تعریف شود :



همانطور که در بالا نشان دادیم ,  تبدیل T برابر تفاضل نقاط تبدیلی است . برای مثال .




از این رو , بردار تبدیل شده ,  T’ , هنوز مماس بدنه باقی می ماند . از طرف دیگر , بردار نرمال تبدیل شده , N’,  دیگر عمود به بدنه نیست !.


همانطور که قبلاً ذکر کردیم , زمانیکه بردار ها تبدیل می شوند ما می توانیم فقط اولین سه جزء از بردار را درنظر بگیریم و زیر ماتریس های 3*3 بالایی . ما نیاز به یک ماتریس جدید داریم برای تبدیل بردار N . یک ماتریس که این را تضمین می کند , بعد از اینکه تبدیل شد , N’  عمود بر T باقی می ماند . بگذارید این ماتریس 3*3 را G صدا بزنیم و ماتریس 3*3 که  T را تبدیل می کند M صدا بزنیم . 
ما می دانیم بعد از تبدیل هر دو بردار آنها باید عمود باقی بمانند , از این رو ضرب نقطه ایی آنها باید صفر باشد پس داریم :



بازنویسی ضرب نقطه ایی همانند ضرب ماتریسی ما داریم :



اگر



سپس




که ما می دانیم صحیح است . از این رو , ارتباط بین  G و M هست :



بنابراین , ضرب بوسیله معکوس ترانهاده ماتریس M از هر دو طرف ما داریم :




بنابراین , ماتریس نرمال , ماتریس G , باید معکوس ترانهاده M باشد , برای مثال معکوس ترانهاده زیر ماتریس 3*3 بالا از ماتریس 4*4 برای تبدیل نقاط بکار برده می شود . 
یک ماتریس متعامد نامیده می شود اگر :




یا




اگر M متعامد باشد سپس :



که ما می توانیم آنرا در بعضی موارد ساده کنیم .


یک ماتریس متعامد تمام سطر ها (ستون ها) دارای اندازه واحد هستند , و اینها متقابلاً عمود هستند . شروع از یک ماتریس , همانند ماتریس همانی , تبدیلات هندسی برای چرخش و جا به جایی , این خصوصیات را نگه می دارد . چنین چیزی برای تغیر مقیاس (اندازه) کاربردی نیست . که مقدار (ارزش) سطر (ستون) بردار های ماتریس تغییر می کند .
بنابراین , اگر ما فقط از چرخش و جابه جایی استفاده کنیم ما تضمین می کنیم ماتریس های ما همیشه متعامد هستند , و ما می توانیم تمام بردار ها را تبدیل کنیم , شامل نرمال ها , با همان ماتریس ها .

برای اطمینان از اینکه سایه زن ها که در این آموزش ها نمایش داده شد توانایی تغییر مقیاس دارند , ما قصد داریم از ماتریس نرمال (3*3) برای تبدیل بردارها (1*3) و ماتریس های معمولی (4*4) به تبدیل تمام بردار های دیگر (4*1) و نقاط.

----------


## amin1softco

همانطور که در قسمت قبلی بحث شد ,  برای بدست آوردن داده های قطعه داده ها برای هر رأس محاسبه و جاسازی شده اند . 

procedure های جاسازی به خوبی کار می کنند تقریباً در هر شرایطی . هر چند , اینجا موردی است که جاسازی می تواند موجب مشکل ما شود : ترکیب بردار نرمال!

نرمال ها در محتوی CG باید دارای طول واحد باشند . این مورد معمولی زمانیکه یک مدل را به برنامه وارد میکنیم , اما یک نیاز نیست . بنابراین خصوصیت  نرمال طول واحد از یک رأس تضمین نشده 



طول باقی مانده . این می تواند یک مسئله باشد اگر این بردار ها جاسازی شوند . شکل زیر را که نمایشگر بردرا های جاسازی شده است را در نظر بگیرید.



در شکل , رأس جاسازی شده در میان باید دارای یک جهت عمودی باشد همانطور که می بینیم , بردار بزرگ تر تأثیر بیشتری روی جهت بردار جاسازی شده دارد . در موارد کلی , برای حل این مسئله ما می توانیم نرمال بردرا نرمال بعد از تبدیل آن در رأس سایه زن است . 

اینجا هر چند موقعیت ها که بردرا تبدیل شده تضمین نگهداشتن آن قبل از تبدیل طول است . اگر فقط تبدیلات و چرخش ها استفاده شده در مدل و ماتریس دید است , یا دقت بیشتری اگر این ماتریس ها متعامد هستند . قسمت قبلی را ببینید , سپس طول بردار نرمال نگه داشته می شود .

در نتیجه , ما می توانیم اجتناب نماییم از نرما کردن بردار نرمال در سایه زن رأس اگر ماتریس نرمال متعامد باشد و ما مطمئن باشیم برنامه در حال تغذیه سایه زن رأس با بردار های نرمال . در غیر اینصورت , یا فقط روی طرف امن است , نرمال کردن بردار نرمال بعد از تبدیل آن در سایه زن رأس است .

زمانیکه بردار می رسد به سایه زن قطعه ما مجبور خواهیم بود آنرا دوباره نرمال کنیم ! چرا؟ زیرا جاسازی نرمال کردن بردار های نرمال یک جهت خوب را تضمین می کند , اما در موارد کلی مقدار (ارزش) اشتباه است ! شکل زیر را چک کنید :




همانطور که دیده می شود , اگر دو بردار بی نهایت دارای طول واحد باشند , سپس بردار میانه دارای مقدار کمتری است . برای حل این مشکل ما  مجبوریم  رأس وارد شوند در سایه زن قطعه را نرمال کنیم . 

ما می توانیم از این اجتناب کنیم ؟ بله , اما فقط اگر تمام رئوس دارای نرمال یکسان باشند , در این مورد جاسازی نرمال ها همیشه برابر است .


راه امن تر , همیشه  نرمال کردن بردار نرمال در هر دو سایه زن است . در سایه زن رأس بعد از تبدیل آن , و در سایه زن قطعه قبل از انجام هر کاری با آن . مطلع باشید که تأثیر نهایی از غیر-نرمال ممکن است بدیهی نباشد . بنابراین بیشتر دقت کنید زمانیکه سعی در بهبود عملکرد  در هزینه ,جلوگیری از این نرمال کردن ها دارید . 
تمام بردار های دیگر می توانند محاسبه شوند با تفاضل دو نقطه , قسمت قبلی را ببینید , جاسازی را بدرستی انجام دهید , از این رو نرمال کردن در سایه زن رأس نیاز نیست .

----------


## amin1softco

قبل از اینکه ما شروع به نمایش سایه زن اپن جی ال کنیم , ما می خواهیم از میان نرم افزار openGL بگذریم که استفاده می کنیم , حداقل در اولین سایه زن ها . برای پوشش تعدادی از قابلیت های از دست رفته در نسخه های هسته اپن جی ال , و جلوگیری گسترش یافتن بیش از حد کد ما از دو کتابخانه خیلی ساده استفاده می کنیم : ریاضی و سایه زن ها

بنابراین اجازه بدهید یک نگاهی به تعدادی از بیت های در نرم افزار اپن جی ال بیاندازیم .

*تابع main*
اجازه دهید با تابع main شروع کنیم . برای ایجاد محتوی اپن جی ال و فراهم کردن رابط ویندوزی می خواهیم به freeGLUT متوسل شویم . برای دسترسی به الحاقی ها و قابلیت اپن جی ال ما GLEW را بر گزیدیم .

procedure مقدار دهی اولیه شامل در خواست GLUT به ساخت محتوی می کند , همانطور که در قسمت مقدار دهی اولیه توضیح داده شد .
int main(int argc, char **argv) {
 
//  GLUT initialization
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RG  BA|GLUT_MULTISAMPLE);
 
    glutInitContextVersion (3, 3);
    glutInitContextProfile (GLUT_CORE_PROFILE );
    glutInitContextFlags(GLUT_DEBUG);
 
    glutInitWindowPosition(100,100);
    glutInitWindowSize(512,512);
    glutCreateWindow("Lighthouse3D - Simple Shader Demo");
    ...

ما سپس فرآیند ثبت فراخوانی برگشتی را انجام می دهیم :
   ...
//  Callback Registration
    glutDisplayFunc(renderScene);
    glutReshapeFunc(changeSize);
    glutIdleFunc(renderScene);
 
//  Mouse and Keyboard Callbacks
    glutKeyboardFunc(processKeys);
    glutMouseFunc(processMouseButtons);
    glutMotionFunc(processMouseMotion);
 
    glutMouseWheelFunc ( mouseWheel ) ;
    ...

در حال حرکت , ما GLEW را مقدار دهی و کمی از اطلاعات درباره محتوی اپن جی ال را را ساختیم .

   ...
//  Init GLEW
    glewExperimental = GL_TRUE;
    glewInit();
 
// print context information
    printf ("Vendor: %s\n", glGetString (GL_VENDOR));
    printf ("Renderer: %s\n", glGetString (GL_RENDERER));
    printf ("Version: %s\n", glGetString (GL_VERSION));
    printf ("GLSL: %s\n", glGetString (GL_SHADING_LANGUAGE_VERSION));
    ...

تا کنون , آن بیشتر یا کمتر چیز های استاندارد که ما  مکراراً می بینیم . الان ما آماده اییم برای شروع واقعی انجام کار های خودمان هستیم . ما قصد داریم توابع بر پاسازی را صدا بزنیم .یکی برای سایه زن های , یکی برای مقدار دهی اولیه بافر هایمان و تعدادی از تنظیمات اپن جی ال , و سرانجام یک تابع برای مقدار دهی اولیه کتابخانه VSL که استفاده می کنیم . سپس ما می خواهیم glutMainLoop (حلقه اصلی) را صدا بزنیم .

 ...
    if (!setupShaders())
        return(1);
    initOpenGL()
    initVSL();
 
    //  GLUT main loop
    glutMainLoop();
 
    return(0);
}

و آن از تابع اصلی ما نتیجه می شود . 

*بر پایی تنظیمات سایه زن* 
تنظیمات سایه زن در مثال های ما بوسیله VSShaderLib برای ساده کردن کد ها انجام می شود . کد با بار گذاری (لود) و کامپایل هر فایل کد منبع , برای هر دو سایه زن رأس و قطعه شروع می شود . مثال سلام جهان اولین پست را ببنید .

فراخوانی prepareProgram برنامه را پیوند می دهد . و اگر با موفقیت باشد  تمام اطلاعات را دریافت می کند با توجه به کاربرد متغیر های  uniform نمایش در سایه زن . این بعداً برای تعیین مقدار آن متغیرها سودمند است . 
ما سپس infoLog (اطلاعات گزارشی) را برای هر دو سایه زن های مجزا و برنامه , و مقدار بازگشتی نشان می دهیم . اگر ما یک برنامه معتبر داشته باشیم . 
کد این تابع به صورت زیر است :
GLuint setupShaders() {
 
    // Shader for drawing the cube
    shader.init();
    shader.loadShader(VSShaderLib::VERTEX_SHADER, "shaders/helloWorld.vert");
    shader.loadShader(VSShaderLib::FRAGMENT_SHADER, "shaders/helloWorld.frag");
 
    // set semantics for the shader variables
    shader.setProgramOutput(0,"outputF");
    shader.setVertexAttribName(VSShaderLib::VERTEX_COO  RD_ATTRIB, "position");
 
    shader.prepareProgram();
 
    printf("InfoLog for Hello World Shader\n%s\n\n", shader.getAllInfoLogs().c_str());
     
    return(shader.isProgramValid());
}

*اپن جی ال و مقدار دهی اولیه بافر* 
در این تابع مقداردهی اولیه بیشتری نیاز داریم برای نرم افزارمان . این شامل , محاسبات اولیه موقعیت دوربین بر پایه مختصات  کروی (spherical)  می شود . بعضی تنظیمات اپن جی ال و ایجاد (VAO) شی آرایه رأس می شود . 

موقعیت دوربین در یک کره وسط مبدأ و با درجه r است .  و با مختصات کروی (alpha, beta, r), تعریف شده . تمام متغیر های سراسری , و مأوس این متغیر ها را کنترل می کنند . قبل از اینکه ما دوربین را در تابع رندر برپاسازیم (نصب کنیم ) ما نیاز به تبدیل آنها به مختصات دکارتی داریم . این باید هر زمان که دوربین حرکت می کند انجام شود, و اینجا , مقدار دهی اولیه برای دریافت مقدار اولیه کارتزین است .

سپس در ادامه به تعدادی از مقدار دهی های اولیه معمولی اپن جی ال می رسیم , از قبیل فعال کردن culling, نمونه گیری چندگانه multisampling و تست عمق .

آخرین مرحله برپاسازی شی آرایه رأس است که می خواهیم شامل شکل هندسی ما , در این مورد یک مکعب که داده ها در فایل هدر آن تعریف شده برسیم . 

ما شروع به تولید  VAO  و مقیدسازی آن می کنیم . سپس ما چهار بافر ایجاد میک نیم برای نگهداری داده ها , سه تا برای خصوصیات رأس و یکی برای شاخص ها (فهرست ها).

void initOpenGL()
{
    // set the camera position based on its spherical coordinates
    camX = r * sin(alpha * 3.14f / 180.0f) * cos(beta * 3.14f / 180.0f);
    camZ = r * cos(alpha * 3.14f / 180.0f) * cos(beta * 3.14f / 180.0f);
    camY = r *                               sin(beta * 3.14f / 180.0f);
 
    // some GL settings
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glEnable(GL_MULTISAMPLE);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
 
    // create the VAO
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
 
    // create buffers for our vertex data
    GLuint buffers[4];
    glGenBuffers(4, buffers);
 
    //vertex coordinates buffer
    glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(VSShaderLib::VERTEX_COOR  D_ATTRIB);
    glVertexAttribPointer(VSShaderLib::VERTEX_COORD_AT  TRIB, 4, GL_FLOAT, 0, 0, 0);
 
    //texture coordinates buffer
    glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);
    glEnableVertexAttribArray(VSShaderLib::TEXTURE_COO  RD_ATTRIB);
    glVertexAttribPointer(VSShaderLib::TEXTURE_COORD_A  TTRIB, 2, GL_FLOAT, 0, 0, 0);
 
    //normals buffer
    glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(normals), normals, GL_STATIC_DRAW);
    glEnableVertexAttribArray(VSShaderLib::NORMAL_ATTR  IB);
    glVertexAttribPointer(VSShaderLib::NORMAL_ATTRIB, 3, GL_FLOAT, 0, 0, 0);
 
    //index buffer
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[3]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(faceIndex), faceIndex, GL_STATIC_DRAW);
 
    // unbind the VAO
    glBindVertexArray(0);
}

*مقدار دهی اولیه VSL* 
VSL, در کتابخانه ریاضی خاص , نیاز به کمی مقداردهی اولیه دار د برای تسهیل ارتباطات با سایه زن ها است . تنظیمات uniform ها برای ماتریس ها ما قصد داریم استفاده انجام شده با فراخوانی تنها یک تابع همانطور که در تابع رندر دیدیم , اما بمنظور انجام ما نیاز به تعریف تعدادی مفاهیم معنایی برای متغیر های uniform  در سایه زن ها تعریف شده . در اینجا ما در نظر می گیریم سایه زن ها تمام ماتریس هایuniform مارا تعریف می کند   درون یک بلاک نام ماتریس ها .

void initVSL() {
    vsml = VSMathLib::getInstance();
    // tell VSL the uniform block name
    vsml->setUniformBlockName("Matrices");
    // set semantics for the matrix variables
    vsml->setUniformName(VSMathLib::PROJ_VIEW_MODEL, "pvm");
    vsml->setUniformName(VSMathLib::NORMAL, "normal");
    vsml->setUniformName(VSMathLib::VIEW_MODEL, "viewModel");
    vsml->setUniformName(VSMathLib::VIEW, "view");
}

*تابع تغییر اندازه* 

دریچه دید به کل پنجره تنظیم شده است . سپس تابع استفاده از کتابخانه ریاضی برای تنظیم به ماتریس پروجکشن . توابع این کتابخانه خیلی شبیه به کسانی است که OpenGL و GLU را بد دانسته اند . مستندات را برای اطلاعات بیشتر ببینید .
void changeSize(int w, int h) {
 
    float ratio;
    // prevent a divide by zero, when window is zero height
    if(h == 0)
        h = 1;
    // set the viewport to be the entire window
    glViewport(0, 0, w, h);
    // set the projection matrix
    ratio = (1.0f * w) / h;
    vsml->loadIdentity(VSMathLib::PROJECTION);
    vsml->perspective(53.13f, ratio, 0.1f, 1000.0f);
}

*تابع رندرینگ* 
این تابع با پاک سازی رنگ و بافر های عمق شروع می شود . , ماتریس همانی را بار گذاری کنید روی مدل و ماتریس های دید , تنظیم به دوربین , سوال  کتابخانه ریاضی برای ساخت دسترسی ماتریس ها از سایه زن ها , و سپس مقید سازی و رندر کردن VAO ماست . 

void renderScene(void) {
 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // load identity matrices
    vsml->loadIdentity(VSMathLib::VIEW);
    vsml->loadIdentity(VSMathLib::MODEL);
    // set the camera
    vsml->lookAt(camX, camY, camZ, 0,0,0, 0,1,0);
    // use our shader
    glUseProgram(shader.getProgramIndex());
    // send the matrices to the uniform buffer
    vsml->matricesToGL();
    // draw VAO
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, faceCount*3, GL_UNSIGNED_INT, 0);
     //swap buffers
    glutSwapBuffers();
}

وادامه آن!  برای دیدن اولین مثال سایه زن در قسمت بعدی .

----------


## amin1softco

در زبان برنامه نویسی برنامه _سلام جهان_ ساده ترین چیزی است که می توانیم بنویسیم که تعدادی نتیجه برای ما فراهم می کند , اینجا حتی صفحه ویکی پدیا در باره آن است ! بطور سنتی خروجی این برنامه "سلام جهان" است , اما  زمانیکه ما درباره سایه زن ها صحبت می کنیم . ما فقط انتظار خروجی شکل هندسی به جای آن داریم .

خط تولید ما قصد دارد یک سایه زن رأس و یک سایه زن قطعه داشته باشد  . سایه زن رأس موقعیت یک رأس را دریافت می کند , در فضای محلی , و آنرا به فضای برش clip space تبدیل میکند . قسمت فضاها و ماتریس ها راببینید . برای انجام این تبدیلات از ماتریس _مدل-دید-پروجکشن_ استفاده خواهیم کرد . 
رئوس پردازش شده سپس با استفاده از متغیر داخلی gl_Position,جاسازی خواهند شد  قسمت سایه زن رأس را ببینید .

تصویر گری سپس قطعات را تولید می کند در سایه زن قطعه . این سایه زن تنها خروجی یک رنگ  برای هر قطعه پردازش می کند .
سایه زن رأس با تعریف نسخه شروع می شود 
#version 330
سپس ما متغیر های uniform را تعریف می کنیم . در این مورد ما یک ماتریس 4*4 داریم برای ذخیره ماتریس مدل-دید-پروجکشن  به نام pvm.  ما قصد داریم از یک بلاک  uniform استفاده کنیم برای ذخیره ماتریس . این احتمالاً برای یک ماتریس تنها بدرد نمی خورد اما در قسمت آموزش آینده این بلاک رشد خواهد کرد برای جا دادن ماتریس های بیشتر .
layout (std140) uniform Matrices {
    mat4 pvm;
} ;

بعداً ما خصوصیات رأس   ورودی  را تعریف می کنیم . در این مورد ما فقط نیازمند مختصات رأس هستیم که یک vec4 است .

in vec4 position;

سرانجام ما می توانیم بنویسیم تابع  main . تنها چیزی این تابع انجام خواهد داد محاسبه متغیر داخلی  gl_Position,  است با ضرب در ماتریس pvm بوسیله مختصات رأس است .
void main()
{
    gl_Position = pvm * position ;
} 

سایه زن کامل به صورت زیر است :
#version 330
 
layout (std140) uniform Matrices {
    mat4 pvm;
} ;
 
in vec4 position;
 
out vec4 color;
 
void main()
{
    gl_Position = pvm * position ;
} 

سایه زن قطعه حتی ساده تر است . آن همچنین با شماره نسخه شروع می شود و متغیر های خروجی را تعریف می کند که  بطور پیش فرض رنگ است ما روی صفحه خواهیم دید و یک رنگ ثابت را در متغیر ها می نویسد .
#version 330
 
out vec4 outputF;
 
void main()
{
    outputF = vec4(1.0, 0.0, 0.0, 1.0);
} 

و آن! نتیجه پایانی , یک مکعب رنگی ثابت است , خیلی مهیج نیست , اما این نقطه شروع برای بیشتر سایه زن هاست که در این قسمت آموزشی داریم .

----------


## amin1softco

در مثال قبلی ما دیدیم چگونگی تبدیل هندسی فرستاده می شود از نرم افزار به مدل ترسیم با یک رنگ تعریف شده همانند ثابت در سایه زن قطعه . در اینجا ما قصد داریم راه های آسانتری برای تنظیم رنگ برای یک مدل سه بعدی است .

اولین رهیافت این است که در رنگ ها را به عنوان خصوصیت رأس در نظر بگیرید. این بدان معناست که , در طرف نرم افزاری , ما باید یک بافر دیگر ایجاد کند برای نگهداشتن مقدار رنگ برای هر رأس , و اضافه کردن این بافر به شی آرایه رأس . در سایه زن رأس ما می خواهیم این فراخوانی خصوصیت رأس جدید به عنوان colorشود,و آنرا به عنوان یک خصوصیت تعریف می کنیم, با استفاده از کلید واژه  in . ما همچنین قصد داریم یک خروجی به نام colorV,  تعریف کنیم  و همچنین قصد نسبت دادن مقدار خصوصیت color, به آن بنابراین آن مقدار از سایه زن قطعه فرستاده می شود .

خطوط مرتبط بهم در سایه زن رأس پر رنگ شده اند :
#version 330
 
layout (std140) uniform Matrices {
    mat4 pvm;
} ;
 
in vec4 position;
in vec4 color;
 
out vec4 colorV;
 
void main()
{
    colorV = color;
    gl_Position = pvm * position ;
}

یک طرح (اولیه) گرافیکی , مانند یک مثلث , که تعریف شده با خصوصیات , به نام  position و در این مورد ,  color. 
رنگ برای هر پیکسل درون مثلث نتیجه جاسازی بر پایه رنگ های رئوس  و مقادیر ذخیره شده روی gl_Position است . برای نموننه , در یک مثلث اگر هر رأس دارای یک رنگ متمایز  شامل یکی از کانال های RGB  باشد . نقطه میانی از مثلث خاکستری خواهد بود . 
زمانیکه متغیر رنگ سرانجام به سایه زن قطعه می رسند سایه زن مقدار جاسازی شده را نگه می دارد . سایه زن قطعه نیازی به انجام هیچ محاسبه ایی ندارد , و تنها تفاوت در این مورد در قسمت آموزشی قبلی این است که خروجی سایه زن قطعه به ورودی قطعات colorV  به جای یک ثابت نسبت داده می شود. 

#version 330
 
in vec4 colorV;
 
out vec4 outputF;
 
void main()
{
    outputF = colorV;
}

یکی از مکانیسم های تطبیق برای متغیر های دربین سایه زن ها بر پایه نام است . متغیر دارای نام یکسان در هر دو سایه زن ها است .  colorV, به عنوان out در سایه زن رأس  و in در سایه زن قطعه تعریف شده است . 

یک تنوع رنگ برای هر رأس یک رهیافت متداول نیست زیرا در بیشتر موارد رنگ یک مدل , یا قسمت های آن , ثابت است و انتقال یک رنگ ثابت به عنوان یک خصوصیت اتلاف پهنای باند و حافظه است زمانیکه رنگ باید برای هر رأس تکرار شود . علاوه بر این , موقعی که الگوریتم های سایه پیچیده تری در نظر بگیریم , “رنگ" یک شی مشخص می شود , نه بوسیله یک vec4 تنها , اما بوسیله یک مجموعه از خصوصیات , از این رو هر رأس ممکن است نیاز به حافظه غیر ضروری زیادی نیاز داشته باشد . 
معمولاً هر فراخوانی ترسیم  OpenGL دارای یک رنگ که وابسته به آن استع و برای پیاده سازی این ما به uniform ها متوسل می شویم . قبل از هر فراخوانی ترسیم ما می توانیم رنگ نسبت داده شده را  بوسیله یک متغیر uniform مشخص کنیم.  بدون توجه به تعداد رأس.

بوسیله این رهیافت , رنگ برای هر رأس در فراخوانی ترسیم ثابت است, از این رو جاسازی نیاز نیست . ما می توانیم از متغیر های uniform استفاده کنیم در سایه زن قطعه , پرش از مرحله جاسازی برای این مقادیر است .

سایه زن رأس به صورت زیر است :
#version 330
 
layout (std140) uniform Matrices {
    mat4 pvm;
} ;
 
in vec4 position;
 
void main()
{
    gl_Position = pvm * position ;
}

سایه زن قطعه ما نیاز به تعریف یک متغیر  uniform برای نگهداشتن رنگ است , color.  این متغیر می تواند از طریق نرم افزار تعیین شود . تا زمانیکه ما می توانیم هر رنگ خاص را تعریف کنیم , برای نمونه قرمز (1,0,0,1), بطور پیش فرض است . 

سایه زن قطعه به صورت زیر است :
#version 330
 
uniform vec4 color = vec4(1.0, 0.0, 0.0, 1.0);
 
out vec4 outputF;
 
void main()
{
    outputF = color;
}

از طرف OpenGL ما دارای یک وظیفه جدید در این مثال هستیم : نشاندن متغیر uniform نmyColor . برای دستیابی به این ما اول نیاز به پرس  و جو مکان متغیر uniform و استفاده از مکان برای انتقال مقدار رنگ مورد نیاز به سایه زن هستیم . همانطور که در قسمت متغیر های  Uniform این با کد زیر انجام شد :
float myFloats[4] = {1.0f, 0.8f, 0.1f, 1.0f};
// p is the program's name
GLint myLoc = glGetUniformLocation(p, "color");
glProgramUniform4fv(p, myLoc, 1, myFloats);

با VSL این کمی ساده تر می شود زمانیکه کتابخانه خودکار مکان را  برای هر متغیر variable ردیابی می کند . در کد OpenGL قبلی تمام چیزی که نیاز داشتیم تغییر/اضافه خط های پر رنگ شده در تابع مسئولیت برای راه اندازی سایه زن ها بود .
GLuint setupShaders() {
 
    // Shader for models
    shader.init();
    shader.loadShader(VSShaderLib::VERTEX_SHADER, "shaders/color.vert");
    shader.loadShader(VSShaderLib::FRAGMENT_SHADER, "shaders/color.frag");
 
    // set semantics for the shader variables
    shader.setProgramOutput(0,"outputF");
    shader.setVertexAttribName(VSShaderLib::VERTEX_COO  RD_ATTRIB, "position");
 
    shader.prepareProgram();
 
    float c[4] = {0.0f, 1.0f, 0.0f, 1.0f};
    shader.setUniform("myColor", c);
 
    printf("InfoLog for Hello World Shader\n%s\n\n", shader.getAllInfoLogs().c_str());
 
    return(shader.isProgramValid());
}

همانطور که ما با رنگ بازی می کنیم , ما قصد کاوش گزینه دیگر که در این مورد بطور خاصی جالب است . مدل ما از  قسمت آموزشی قبلی  یک مکعب است که مختصات محلی بین 0.0 و 1.0 متنوع است . این یک تطبیق در مسیر رنگ های متفاوت است !!!! هر دو دارای تطابق سه جزء و بازه ها هستند . بنابراین مکعب ما چه شکلی می شود اگر به جای یک رنگ تعریف شده توسط کاربر ما خروجی موقعیت ها در مختصات محلی را بدهیم ؟

سایه زن رأس برای تلاش این نیازمند است که یک متغیر برای دخیره رأس محلی بکار ببریم و انتقال آن به سایه زن قطعه . این شبیه  به اولین نسخه است , تنها تفاوت در این است که مقدار شروع به نسبت دادن به این متغیر جدید می کند نه خصوصیت رنگ اما مختصات رأس ورودی , یا position.

#version 330
 
layout (std140) uniform Matrices {
    mat4 pvm;
} ;
 
in vec4 position;
 
out vec4 color;
 
void main()
{
    color = position;
    gl_Position = pvm * position ;
}

سایه زن قطعه همانند اولین رهیافت است :
#version 330
 
in vec4 color;
 
out vec4 outputF;
 
void main()
{
    outputF = color;
}
و نتیجه آخر شکل زیر است .

بیشتر از فقط بازی با مسیر هایی یک مکعب می شود , این آخرین رهیافت می تواند یکی از قدرتمند ترین استراتژی ها لرفع خطا در دست ما باشد . بر خلاف  C, C++‎‎,  یا بیشتر زبان های دیگر , کنسولی برای چاپ پیام های خطا یا مقادیر خاص نیست . درست است , ما می توانیم یک سیستم پیچیده با بازخورد تبدیل و لود/ دخیره تصاویر برای گرفتن هر قطه اصطلاعات که می خواهم از یک سایه زن استفاده کنیم , هنوز چیزی نیست برای خروجی رنگ برای  رفع کردن بیشتر مشکلات 
نتیجه بالا به ما می گوید هنوز هیچ چیز بر خروجی رنگ  برای رفع عمده مشکلات رایج غلبه نمی کند . 

نتیجه بالا به ما می گوید ما مختصات صحیح را گرفتیم در سایه زن رأس ! این همیشه بدیهی نیست ماند این مورد . هنوز داده خروجی , مختصات ها در این مورد , به عنوان رنگ در یک مسیر به رفع عیب یک سایه زن .

کد منبع و پروژه Visual Studio 2010 را می توانید از اینجا دریافت کنید . در کد منبع سایه زن ما پیدا کردیم رهنمود پیش پردازش , شبیه به آنچه در C یا C++‎‎   است . این به ما اجازه جمع و جور کردن دو سناریو آخر در یک جفت از شیدر ها را می دهد .فقط  خط تعریف در هردو سایه زن برای دیدن هر دو رهیافت را کامنت/خارج از کامنت کنید.

----------


## amin1softco

*نور پردازی*

نور پردازی در گرافیک کامپیوتری ضروری است . صحنه ها بدون نور پردازی خیلی صاف به نظر می رسند , تصور شکل اشیاء آنرا سخت می کند . در اینجا ما اساس نور پردازی و مدل های سایه را مرور می کنیم . 

یک مدل نور پردازی چگونگی بازتاب نور روی یک نقطه خاص را تعیین می کند . رنگ دیده شده روی آن نقطه به تعداد پارامتر ها بستگی دارد , برای نمونه , جهت نور , جهت ببیننده , خصوصیات ماده , به نام چندتاست .

یک مدل سایه به چگونگی کاربرد مدل نورپردازی برای روشن کردن سطح مرتبط است . برای نمونه , 
*سایه هموار Flat* : ما می توانیم مقدار یک رنگ را برای هر مثلث مجزا حساب کنیم ,  
*سایه گوارد Gouraud* : برای رئوس یک مثلث رنگ را حساب کنیم و  مقدار رنگ برای نقاط درون مثلث را جاسازی نماییم .
*سایه Phong* : یا حتی  رنگ را برای تمام نقاط سطح محاسبه کنیم . 

نورپردازی عمیقاً به رنگ وابسته است . زمانی که یک شی روشن است ما رنگ آنرا می بینیم در غیر اینصورت اگر نوری به آن نرسد کاملاً مشکلی به نظر می رسد .

رنگ در CG (گرافیک کامپیوتری) از چندین کلمه ترکیب شده , با نام :

*diffuse* (نور پخشی): نور بازتاب شده با یک شی در هر جهت . این چیزی است که ما معمولاً  رنگ یک شی می نامیم .

*ambient* (نور محیطی ) : برای شبیه سازی نور منعکس شده بکار می رود . نواحی که نور مستقیم نمی تواند پیدا کند را پر می کند , و با این وسیله باعث می شود آن نواحی خیلی تاریک نشوند . معمولاً این مقدار متناسب با رنگ پخشی است .

*specular* (وابسته) : این نور بازتاب ها قوی تر در جهت خاص را می گیرد , معمولاً در بازتاب نور جهت بردار در اطراف سطح نرمال . این رنگ به رنگ پخشی وابستگی ندارد .

*emissive* (انتشاری) : شی از خود نور منتشر می کند .

عکس بعدی تأثیر اولین سه جزء رنگ زمانی که یک شی روشن است نشان می دهد . برای تعریف ماده(material) اشیاء ما برای هر یک از اجزاء بالا  مقادیر را تعریف می کنیم .

شکل از چپ به راست : 
(پخشی) diffuse 
; (محیطی) ambient 
;(محیطی) + (پخشی) diffuse + ambient 
; (پخشی) + (محیطی) + (وابسته)  diffuse+ambient+specular

نور همچنین در بسیاری بسته ها آماده است  . رایج ترین انواع نوری و آسان ترین برای پیاده سازی عبارتند از :
نور جهتی , نقطه ایی و نورافکنی directional, point, و spotlights. 

در یک نور جهتی , ما فرض می کنیم تمام اشعه های نوری موازی هستند , همانند زمانی که نور در مکان بی نهایت قرار گرفته , و فاصله مورد توجه نیست . برای نمونه , برای تمام اهداف عملی , برای یک بیننده روی کره زمین , نوری که از خورشید می رسد نور جهتی است . این بدان معناست که نور جهتی برای تمام رئوس  و قطعات ثابت است , که پیاده سازی این نوع را بسیار ساده کرده است . 

نورهای نقطه ایی , اشعه های نوری را در تمام جهات منتشر می کنند , در ست شبیه یک لامپ عادی , یا حتی خورشید زمانیکه ما  سیستم خورشیدی  را مدل می کنیم .

نور افکن ها نقاط نورانی هستند که فقط نور را در مجموعه جهات خاصی منتشر می کنند . یک رهیافت معمول این است که حجم نور در یک مخروط در نظر بگیریم که نوک آن موقعیت نور است . از این رو , یک شی زمانی روشن می شود که درون مخروط باشد .



شکل : انواع نور ها . از چپ به راست : جهتی , نقطه ایی و نور افکنی 

چندین رهیافت برای محاسبه بازتاب نور بوسیله یک شی به سمت دوربین یا چشم وجود دارد ,برای محاسبه این مقادیر برای نقطه خاص ما نیاز به دانستن مکانی که نور از آن می آید داریم , و زاویه بین بردار نور و سطح نرمال (یک بردار عمود بر سطح) , دوربین کجاست , چیز های مشخص دیگر . معادله ایی که به ما اجازه محاسبه نور را می دهد یا بازتاب نور برای نقطه خاص دیکته شده که تنظیمات بکار می برند و چطور.

همانطور که قبلاً ذکر کردیم گزینه های ما محدود به معادله نور پردازی نیستند . یک مثلث را در نظر بگیرید ما می توانیم نور را برای هر رأس محاسبه کنیم , و سپس جاسازی را  برای هر قطعه درون مثلث بکار ببریم. یا ما می توانیم تمام داده های رأس موجود را جاسازی کنیم , انها را به سایه زن قطعه ارسال کنیم و سپس رنگ را برای هر قطعه محاسبه کنیم . این مدل سایه متعامد است با معادله نور پردازی , در صحنه ما می توانیم هر معادله را با هریک از مدل های سایه بالایی برای روشن شدن صحنه ترکیب کنیم . 

وقتی قابلیت های ثابت (fixed functionality) موجود است , نیازی به نوشتن سایه زن ها نیست , نور پردازی برای هر رأُس محاسبه می شود . رنگ هر رأس سپس به فاز تصویر گری و ترکیب (جاسازی) ارسال می گردد که رنگ برای هر قطعه محاسبه شده  جاسازی می شود . قابلیت های ثابت یک پیاده سازی از مدل سایه  Gouraud هستند . نتیجه زیاد دلچسب  نیست  تا زمانیکه  ترکیب (جاسازی) بهترین مسیر برای محاسبات نور پردازی  که می تواند متفاوت باشد از راه های درون یک چند ضلعی . یک نور افکنspotlight  را در مرکز یک مثلث تصور کنید, اما هیچ یک از رئوس . نتیجه ممکن است مثلث روشن نباشد که کاملاً اشتباه است .

سایه Gouraud ساخته شده حس را بر می گرداند زمانیکه ما نوعاً دارای پیکسل های خیلی بیشتر از رئوس هستیم , و راه قدرت محاسباتی کمتر . امروزه ما قدرت محاسباتی خیلی بیشتری داریم , که به نوبه خود به ما اجازه کاربرد تعداد بزرگ و بزرگتر چند ضلعی ها در یک صحنه را می دهد . این دو خصوصیت با نتیجه خیلی ضعیف از نور پردازی برای هر رأس منجر به روند جاری انجام نور پردازی برای هر رأس می شود . برای مثال مدل سایه Phong .

تمام بالا روی نقطه که روشن است متمرکز است , منبع نور و موقعیت دوربین . اما سایه ها چه می شوند ؟ که مفهوم تست کردن را می رسانند اگر هر شی دیگر در این مسیر از نقطه که قرار است روشن شود تا هر منبع نور. و چرا اشیاء دیگر به خودی خود نمی توانند به عنوان منبع نور عمل کنند , بازتاب نوری که به انها برخورد میک ند به سمت نقطه ما , بدینوسیله در رنگ نهایی شرکت کنند ؟ و بازتاب ؟ نورپردازی موضع بسیار پیچیده ایی است , تعداد حتی ممکن است بگویند نور پردازی موضوع گرافیک کامپیوتری است .

به هر حال , ما مجبوریم از یک جا شروع کنیم , بنابراین ما به فرضیات ساده می چسبیم که نورپداری یک نقطه است , بدون در نظر گرفتن هر عکس العمل با اشکال هندسی باقی مانده , فقط دورین و منبع نور . در قسمت بعدی ما قصد داریم ببینیم چگونه سایه زن هایی را بنویسیم که تمام انواع نور هایی که در بالا ذکر کردیم را شبیه سازی کند و هردو برای هر رأس و برای هر نقطه ( pixel) .

کد منبع برای تمام نوع های نور , و مدل های سایه زن , موجود است : L3DLighting.  فایل فشرده شامل یک پروژه Visual Studio 2010 است . برای سعی ترکیب مدل های متفاوت نور/سایه  تابع “setupShaders” را ببینید .

----------

