Shader Structure
Compiler-д танигдах бүтцийг баримтлан шэйдрийг үүсгэнэ. Өөрөөр хэлбэл шэйдрийн код эндээс эхлэнэ , тэнд дуусна гэх мэт. Студид ашигладаг ихэнхи мэргэжлийн шэйдрүүд нь препроцессор макро, нэмэлт файл , гадаад функцуудыг агуулсан төвөгтэй бүтэцтэй байдаг хэдий ч бүгд дараахи үндсэн бүтцийг баримтална.
shader: Төрөл Нэр ( Параметрууд)
{
RSL код;
}
Шэйдр нь өмнөх “Shader Types” хэсэгт дурдагдсан дурын шэйдр байж болно. Шилжилтийн болон гадаргын шэйдрүүд хамгийн өргөн хэрэглэгддэг. Шэйдрийн төрлийг тодорхойлж өгснөөр хөрвүүлэгчид ямар төрлийн шэйдр дээр ажиллах шаардлагатай болсноо мэдэж тухайн шэйдрт хамаатай урьдчилан тодорхойлогдсон хувьсагчдыг бэлддэг. Шэйдрийн төрөл бүрд шаардагдах урьдчилан тодорхойлогдсон хувьсагчдыг дараа нь нэг бүрчлэн авч үзэх болно. Шэйдрийн төрлийг зааж өгөхийн бас нэг чухал тал нь renderer үүнийг хаана юунд ашиглахыг мэддэг болно. Жишээ нь шилжүүлэлтийн шэйдрийг гэрэлд оноож болохгүй, гэрлийн шэйдрийг геометр биетэд оноож болохгүй гэх мэт. (Зарим биетийг талбайтай гэрэл /area light/ болгож болно). Шэйдрийн нэр нь өмнөх сэдвээр үзсэн хувьсагчид нэр оноох дүрэмтэй адил зарчим баримталсан дурын нэр байж болно. Шэйдр болон файлын нэр ижил байх нь практикийн ач холбогдолтой. Жишээ нь шэйдрийн нэр marble бол файлаа marble.sl гэж хадгалах хэрэгтэй.
Энэ аргыг ашигласнаар файл дотор ямар шэйдр агуулагдаж байгааг төвөггүй мэднэ. Эх кодыг агуулсан файл дотор цорын ганц шэйдр байна гэсэн RSL-ийн чухал дүрмийг мөрдлөг болгосноор дээрхи аргыг хэрэглэх бололцоог олж авна. Шэйдрийн нэрийн араас хэрэглэгч шэйдрийн хэсгүүдийг хянаж тохируулахад хэрэг болох параметруудыг (инстанс хувьсагч эсвэл шэйдрийн аргументын жагсаалт ч гэж нэрлэдэг) хагас дугуй хаалтанд өрж тавина.
Хэрэв гантиг чулуу шиг харагдах шэйдр бичнэ гэвэл бол судлын өнгө, өргөн, сарнилт, бүхэл хэсгийн өнгө гэх мэт хэрэглэгчийн тохиргоог агуулсан байх хэрэгтэй. Шэйдрийн дизайныг бүтээхдээ аль хэсгийг параметраар тохируулах бололцоотой байх ямар нь хэрэггүй гэдгийг сайн анхаарах хэрэгтэй. Зарим тохиолдолд хэтэрхий их тохиргоотой байх нь муу шэйдрийн шинж болдог. Параметрыг зарлах нь хувьсагчтай төстэй. Ялгаа нь параметр зарлагдах явцдаа анхны утгаа авах ёстой. Энэхүү оноосон утга нь default утга бөгөөд шэйдрт тохирсон оновчтой үр дүн үзүүлэх утга байвал тохиромжтой. Хэрвээ хэрэглэгч класс зааж өгөөгүй бол параметр хувьсагчид нь автоматаар ижил классд хамаарна. Доорхи жишээн дээр параметрыг хэрхэн зарлахыг харцгаая.
Энэ аргыг ашигласнаар файл дотор ямар шэйдр агуулагдаж байгааг төвөггүй мэднэ. Эх кодыг агуулсан файл дотор цорын ганц шэйдр байна гэсэн RSL-ийн чухал дүрмийг мөрдлөг болгосноор дээрхи аргыг хэрэглэх бололцоог олж авна. Шэйдрийн нэрийн араас хэрэглэгч шэйдрийн хэсгүүдийг хянаж тохируулахад хэрэг болох параметруудыг (инстанс хувьсагч эсвэл шэйдрийн аргументын жагсаалт ч гэж нэрлэдэг) хагас дугуй хаалтанд өрж тавина.
Хэрэв гантиг чулуу шиг харагдах шэйдр бичнэ гэвэл бол судлын өнгө, өргөн, сарнилт, бүхэл хэсгийн өнгө гэх мэт хэрэглэгчийн тохиргоог агуулсан байх хэрэгтэй. Шэйдрийн дизайныг бүтээхдээ аль хэсгийг параметраар тохируулах бололцоотой байх ямар нь хэрэггүй гэдгийг сайн анхаарах хэрэгтэй. Зарим тохиолдолд хэтэрхий их тохиргоотой байх нь муу шэйдрийн шинж болдог. Параметрыг зарлах нь хувьсагчтай төстэй. Ялгаа нь параметр зарлагдах явцдаа анхны утгаа авах ёстой. Энэхүү оноосон утга нь default утга бөгөөд шэйдрт тохирсон оновчтой үр дүн үзүүлэх утга байвал тохиромжтой. Хэрвээ хэрэглэгч класс зааж өгөөгүй бол параметр хувьсагчид нь автоматаар ижил классд хамаарна. Доорхи жишээн дээр параметрыг хэрхэн зарлахыг харцгаая.
surface checker ( float checkerSize = 1.0;
color colorA = color (1,0,0);
color colorB = color (0,1,0);
)
Гадаргууг зурахад шаардагдах шэйдрийн параметрууд нь ихэвчлэн тогтмол утгатай байдаг. Хэрэв хэрэглэгч RIB файл дотроо ижил нэр, төрөлтэй шэйдрийн параметр зааж өгсөн бол шэйдрийг зарлахдаа оноосон тогтмол утгын оронд хэрэглэгчийн зааж өгсөн хувьсагчын утгыг зурах цэг болгон дээр ашиглана. Ингэж зааж өгсөн хувьсагчийг примитив хувьсагч гэж нэрлэдэг. Ийм ажиллагаа нь асар өргөн боломжийг нээж өгдөг. Примитив хувьсагчийн утгыг динамик мөргөлт , particle-ийн үйлчлэл (event), гараар тохируулсан багц keyframe-үүдийн тусламжтай гэх мэт олон аргаар өөрчлөх боломжтой. Хамгийн сүүлийн хэсэг бол шэйдрийн кодыг агуулсан их биеийн хэсэг юм. Энд л шэйдрийн үндсэн үйл ажиллагаа явагддаг. Параметруудын ард байрлах {} тэмдэгтийн хооронд бүх код бичигдэнэ. Шэйдрийн их бие дотор зарлагдсан бүх хувьсагчид хэрэглэгч зааж өгөөгүй тохиолдолд автоматаар хувьсах класст хамаарна.
Operators
RSL нь гадаад сан болон нэмэлт кодыг ашиглан тооцоолох шаардлагатай зарим үйлдлүүдийг үндсэн операторуудын тусламжтай хялбархан гүйцэтгэдэг. RSL нь өөрт агуулагдах өгөгдлийн төрлүүд болон тэдгээр дээр гүйцэтгэх үүргүүдийн талаар сайн мэддэг. Оператор нь хоорондоо ялгаатай өөр төрлийн өгөгдлүүд дээр үйлдэл хийсэн бол хөрвүүлэх явцад алдаа эсвэл анхааруулга заана. Хэрэв анхааруулга гарсан бол тун хянуур хандах хэрэгтэй. Учир нь шэйдр хөрвүүлэгдсэн ч гэсэн үр дүн нь худлаа байх магадлалтай. RSL хэлэнд ашиглагддаг операторууд:
Операторын тэмдэгтийн тайлбар
Операторын тэмдэгтийн тайлбар
= утга оноох
+ нэмэх
- хасах
* үржүүлэх
/ хуваах
Вектор дээр гүйцэтгэх үйлдэл
. Векторын скаляр үржвэр
^ Векторын вектор үржвэр
Матриц дээр гүйцэтгэх үйлдэл
* Матриц үржвэр
/ Урвуу матрицын үржвэр
Тэнцүүгийн тэмдэгийн оператор нь хувьсагчид утга онооход ашиглагдана. +, -, *, ба / операторууд нь математикийн харгалзах үйлдэлүүдтэйгээ ижил. Эдгээр операторууд нь тэмдэгт мөр, массив , зарим массивийн үйлдлүүдээс бусад бүх өгөгдлийн төрлүүд дээр ажиллана. RSL нь хоорондоо нийцгүй, тохиромжгүй өгөгдлүүд дээр үйлдэл хийсэн тохиолдолд сэрэмжлүүлдэг хэдий ч ямар өгөгдөлтэй ажиллаж байгаа , тэдгээрийн хооронд математикийн үйлдэл хэрхэн хийгддэг болохыг сайтар мэдэж байх хэрэгтэй. Доор зарим өгөгдлийн төрлийн баримтлах үндсэн дүрмийг харуулъя.
■ Цэг, вектор, нормал, өнгөн дээр эдгээр операторуудыг ашиглавал харгалзах элемент тус бүрд нь ашигласантай адил. Өөрөөр хэлбэл (2,3,4) ба (4,3,3) гэсэн хоёр векторыг үржүүлбэл (8,9,12) гэсэн вектор үүснэ.
■ Цэгэн дээр вектор нэмбэл нийлбэр нь цэг байна.
■ Вектор дээр вектор нэмбэл нийлбэр нь вектор байна.
+ нэмэх
- хасах
* үржүүлэх
/ хуваах
Вектор дээр гүйцэтгэх үйлдэл
. Векторын скаляр үржвэр
^ Векторын вектор үржвэр
Матриц дээр гүйцэтгэх үйлдэл
* Матриц үржвэр
/ Урвуу матрицын үржвэр
Тэнцүүгийн тэмдэгийн оператор нь хувьсагчид утга онооход ашиглагдана. +, -, *, ба / операторууд нь математикийн харгалзах үйлдэлүүдтэйгээ ижил. Эдгээр операторууд нь тэмдэгт мөр, массив , зарим массивийн үйлдлүүдээс бусад бүх өгөгдлийн төрлүүд дээр ажиллана. RSL нь хоорондоо нийцгүй, тохиромжгүй өгөгдлүүд дээр үйлдэл хийсэн тохиолдолд сэрэмжлүүлдэг хэдий ч ямар өгөгдөлтэй ажиллаж байгаа , тэдгээрийн хооронд математикийн үйлдэл хэрхэн хийгддэг болохыг сайтар мэдэж байх хэрэгтэй. Доор зарим өгөгдлийн төрлийн баримтлах үндсэн дүрмийг харуулъя.
■ Цэг, вектор, нормал, өнгөн дээр эдгээр операторуудыг ашиглавал харгалзах элемент тус бүрд нь ашигласантай адил. Өөрөөр хэлбэл (2,3,4) ба (4,3,3) гэсэн хоёр векторыг үржүүлбэл (8,9,12) гэсэн вектор үүснэ.
■ Цэгэн дээр вектор нэмбэл нийлбэр нь цэг байна.
■ Вектор дээр вектор нэмбэл нийлбэр нь вектор байна.
■ Цэгээс цэгийг хасвал вектор болно.
■ Векторыг тоогоор үржүүлбэл хэмжээ нь өөрчлөгдсөн вектор болно.
■ Векторыг тоогоор үржүүлбэл хэмжээ нь өөрчлөгдсөн вектор болно.
Илүү дэлгэрэнгүй дүрмийг сүүлд нарийвчлан үзэх болно. RSL нь эдгээр операторуудыг тэнцүүгийн тэмдэгийн өмнө тавьж хэрэглэхийг зөвшөөрдөг. Энэ нь эх кодыг энгийн хялбар болгодог. Одоогоор зөвхөн нэг л математик операторыг = оператортай хослуулж болох бөгөөд энэхүү операторын тусламжтай шинэ утга авч байгаа хувьсагч нь зарлагдсан, ямар нэг утга агуулсан байх шаардлагатай. Жишээ нь :
float A = 1; // A хувьсагч утга агуулсан байх шаардлагатай.
A += 4; // ' A = A + 4 ' гэсэн бичиглэлтэй ижил бөгөөд арай богино
A *= 5; // A -ийн утга нь 25 тай тэнцүү
A += 4; // ' A = A + 4 ' гэсэн бичиглэлтэй ижил бөгөөд арай богино
A *= 5; // A -ийн утга нь 25 тай тэнцүү
Вектор үржвэр (^) болон скаляр үржвэр (.) нь вектор, нормал хоёр дээр л ашиглагдана. Өнгө болон цэг дээр дээрхи хоёр операторыг ашиглавал шууд алдаа заана. Энэ хоёр үйлдэл ялангуяа скаляр үржвэр нь шэйдр бичихэд голлох үүрэгтэй. Матриц дээр үржүүлэх болон урвуу матрицаар үржүүлэх гэсэн хоёр үйлдлийг ашиглах боломжтой. Эдгээр нь RSL-д ашиглаж болох бүх операторууд юм. Бусад програмчлалын хэл нь операторыг өгөгдлийн шинэ төрөлд зориулан дахин тодорхойлохыг зөвшөөрдөг. Жишээ нь % операторыг хоёр өнгийг уусгахад ашигладаг. RSL нь операторыг шинээр дахин тодорхойлохыг дэмждэггүй бөгөөд DSO C хэлний нэмэлт механизмыг ашиглаад ч шинээр тодорхойлох боломжгүй.Иймээс л хүмүүс хоёр өнгө холихын тулд нэлээн хэдэн функц бичих шаардлагатай болдог.
Flow Control
Ямар ч програм зохиосон өгөгдлийн урсгалыг хэрхэн хянаж , зохицуулах шийдлийг кодонд зайлшгүй хавсаргах ёстой. Зарим хэл нь өгөгдлийн тусгай төрлүүдэд тохируулсан урсгалыг хянах олон командуудтай байдаг. RSL нь урсгалыг зохицуулах гурван командыг дэмждэг. Үүний нэг нь нөхцөл шалгах нөгөө хоёр нь цикл команд. Урсгалыг хянах командууд нь С хэлтэй яг адилхан дүрмээр бичигддэг. С хэлтэй адил { } хаалтан доторхи багц үйлдлүүдийг урсгалын зарчмаар гүйцэтгэнэ. Хэрвээ илэрхийлэл нь нэг мөрөнд багтаж байвал энэхүү хаалтыг хэрэглэхгүй байж болно. Нөхцөл шалгах үйлдэл нь if-else командын тусламжтай хийгдэнэ. Энэ команд өгөгдсөн нөхцлийг шалгаад шаардлагыг хангаж байвал эхний илэрхийллийг гүйцэтгэнэ. Харин тухайн нөхцлийг хангаагүй бол else дотор агуулагдах командуудыг гүйцэтгэнэ. Else өргөтгөл нь байсан ч болно байхгүй байсан ч болно. if-else команд нь дараахи бичиглэлийн дүрмийг баримтална.( [ ] хаалтан доторхи нь зайлшгүй шаардлагатай биш ):
if ( нөхцөл) {
илэрхийлэл1;
}
[else {
илэрхийлэл2;
}]
Нөхцөл шалгах үйлдэлд ifelse-if гинжин холбоосыг ашиглаж болно. else-if нөхцлийг ашигласан тохиолдолд төгсгөлийг үргэлж else-гээр хийх хэрэгтэй. Аль ч “if” нь заасан шаардлага биелэхгүй бол төгсгөлийн else дээр очно.
if (нөхцөл1){
илэрхийлэл1;
}
else if (нөхцөл2) {
илэрхийлэл2;
}
else {
илэрхийлэл3;
}
If дотор байрлах нөхцөл нь ихэвчлэн Булын илэрхийлэл байдаг. RSL нь булын төрлийг дэмждэггүй боловч булын төрөл дээр хийгддэг үйлдлүүдийг гүйцэтгэж чадна. Өөрөөр хэлбэл 1 (үнэн ) ба 0 (худал) гэсэн утга буцаадаг үйлдлүүд юм. Нөхцөл шалгахад зөвхөн харьцуулах операторуудыг ашиглана гэдэг нь хамгийн чухал дүрэм юм. Өөрөөр хэлбэл С хэл шиг нөхцөл шалгах бололцоогүй:
if (1 - 0) z = z * 2; // алдаатай
Учир нь булын илэрхийлэл байх ёстой газар float илэрхийлэл ашигласан. Түүнчлэн өнгө, цэг гэх мэтийг агуулсан илэрхийлэл байхыг зөвшөөрдөггүй. Энэ дүрэм эсрэгээрээ ч хүчинтэй. Бутархай тоо, өнгө, цэг зэрэг өгөгдлүүдэд 0 эсвэл 1 гэсэн утга буцаадаг булын илэрхийлэл ашиглан утга оноох боломжгүй. Булын илэрхийлэлд ашигладаг харьцуулах операторуудыг харцгаая:
Тэмдэгт Нэр Нөхцөл биелвэл 1-ийг буцаана
== тэнцүү Зүүн тал нь баруунтайгаа тэнцүү бол
!= тэнцүү биш Зүүн тал нь баруунтайгаа тэнцүү биш бол
< бага Зүүн тал нь баруунаасаа бага бол
<= бага буюу тэнцүү Зүүн тал нь баруунаасаа бага болон тэнцүү бол
> их Зүүн тал нь баруунаасаа их бол
>= их буюу тэнцүү Зүүн тал нь баруунаасаа их болон тэнцүү бол
Адил төрлийн өгөгдлийг харьцуулахад ямар ч асуудал байхгүй. Харин өөр төрлийн өгөгдлүүдийг харьцуулахдаа дараахи зүйлсийг анхаарах хэрэгтэй:
■Бутархай ганц өгөгдлийг гурван элемент бүхий өгөгдөлтэй харьцуулахад энэ нь гурван элемент тус бүр дээр гүйцэтгэгдэнэ.
■ Цэгийг өнгөтэй харьцуулж болохгүй.
■ Хоёр матрицыг зөвхөн == ба =! операторын туслалцаатай харьцуулна.
Хамгийн түгээмэл ашиглагддаг if хэллэгийн жишээг харъя. Шэйдрт зураг, текстур оноосон эсэхийг урьдчилан шалгана. Хэрвээ хоосон бол цулгуй өнгө онооно.
varying color myColor;
if (myTexture != "")
{
myColor = texture(myTexture,s,t);
}
else
{
myColor= color (1,1,1);
}
Заримдаа нэгээс олон хэд хэдэн нөхцлийг зэрэг шалгах хэрэгцээ гардаг. Энэ тохиолдолд && (ба) , the || (эсвэл) гэсэн 2 операторыг ашиглана. && оператор нь үйлдлийг зүүнээс баруун тийш чиглэлтэй биелүүлэх бөгөөд энэ гинжин доторхи нэг нөхцөл биелэгдэхгүй бол else-рүү шилжинэ.
color baseColor = color (0.5,0.5,0.5);
if ((myTexture != "") && (textureValue > 0))
{
textureCol = texture (myTexture,s,t);
colOut = mix (baseColor,textureCol,textureValue);
}
else if ((myTexture == "") && (textureValue > 0))
{
colOut = mix (baseColor, color(1,0,0), textureValue);
}
Дээрхи жишээнд үзүүлснээр текстур нь хоосон биш бөгөөд textureValue нь 0-ээс их байвал myTexture-ийн утгыг baseColor-той холино. Эхний нөхцөл биелвэл дараагийнх руу шилжих замаар ажиллана. Эхний нөхцөл хангагдахгүй бол хяналт нь дараагийн нөхцлийг алгасаж шууд else блок руу шилжинэ. myTexture нь хоосон боловч textureValue нь тэгээс их бол baseColor-ийг улаан өнгөтэй холих үйлдэл хийгдэнэ.
if ((Ks <= 0) || (specColor == (0,0,0))
{
Cspec = color (0,0,0);
}
else {
Cspec = specular(V,Nf,roughness) * Ks * specColor;
}
Дээрхи жишээнд харуулснаар гялаан гэрлийн коэффицент (ихэвчлэн Ks) эсвэл гялаан гэрлийн өнгийн (specColor) аль нэг нь 0 буюу хар бол specular тооцооллыг орхигдуулна. || операторыг ашиглаж байхад эхний нөхцөл биелвэл дараагийн нөхцлийг шалгах шаардлага байхгүй. Операторын логикууд болон үр дүнг тооцоолох, ямар нөхцлийг ашиглавал хамгийн оновчтой шийдэл болох зэргийг сурахад бага зэргийн хүчин чармайлт гаргах хэрэгтэй. Оператор бүр хоорондоо ялгаатай бөгөөд үзүүлэх үр дүн ч адил биш.
if ( myValue >0 ) нөхцөл нь if (myValue >= 0) нөхцлөөс ялгаатай.
RSL нь С хэлэнд ашиглагддаг if-else-ийн хялбаршуулсан хэлбэрийг ашиглахыг зөвшөөрдөг. if else ашиглан тун богино команд гүйцэтгэх бол энэ арга нь тун тохиромжтой. Доорхи жишээнд текстур оноохоос өмнө текстур координатын s , t-ийн утгыг нөгөө тийш нь хөрвүүлэх шаардлагатай үгүйг тодорхойлно. Бичих дүрэм нь ( нөхцөл ? илэрхийлэл 1: илэрхийлэл 2) хэлбэртэй байна. Жишээ:
uniform float flips = 0;
uniform float flipt = 0;
varying float ss = (flips == 0 ? s: 1 - s);
varying float tt = (flipt == 0 ? t: 1 - t);
color textureColor = texture ("mytex.tx",ss,tt);
if-else -ийн талаар анхааруулах хамгийн сүүлийн зүйл нь бусад програмчлалын хэлэнд өргөн хэрэглэгддэг доорхи маягийн бүтэц.
if (хувьсагчийнНэр) {үйлдэлүүд .... }
Бусад хэлний сompiler нь хувьсагч NULL болон 0-ээс өөр утгатай бол нөхцөл биелнэ гэж үздэг. Харин RSL-д ингэж бичвэл алдаа заана. Тэгэхээр бутархай эсвэл тэмдэгт мөр үү гэдгээс шалтгаалан дараахи маягаар бичих хэрэгтэй.
if (хувьсагчийнНэр != 0) эсвэл if (хувьсагчийнНэр != “”)
Шэйдр дотор байж болох урсгалыг хянах өөр нэг хэрэгсэл бол for, while итераци бүтцүүд юм. Энэ командууд нь текст илэрхийллийг үндэслэн бүлэг код сегментийг дахин давтан гүйцэтгэхэд хэрэглэгддэг. Хоорондоо төстэй их хэмжээний код агуулсан шэйдр үүсгэхэд эдгээр командууд маш их хэрэгтэй. Жишээ fractal хээ үүсгэдэг шэйдр хийлээ гэж бодъё. Үүний тулд хэд хэдэн өөр өөр хэмжээтэй noise давхаргуудыг цикл ашиглан үүсгэж сүүлд нь нэг текстур болгон нэгтгэнэ. FOR цикл нь дараахи синтаксийг баримтална.
for (initialization, булын илэрхийлэл , loop statement)
{
код.....;
}
initialization гэдэг нь цикл дөнгөж эхлэхэд хувьсагчид анхны утга оноох үйлдэл . Дараа нь булын илэрхийллийн нөхцлийг шалгана. . Хэрэв нөхцөл биелвэл доторхи кодыг гүйцэтгэнэ. Кодыг гүйцэтгэж дууссаны дараа loop statement-ийг гүйцэтгэнэ. Loop statement нь ихэвчлэн цикл эхлэхэд оноосон хувьсагчийн утгыг өөрчлөх илэрхийлэл байдаг. Loop statement-ийг гүйцэтгэсний дараа boolean expression-д байрлах нөхцлийг ахин шалгаад үнэн бол кодыг давтан гүйцэтгэнэ. Энэ процесс нөхцөлт илэрхийлэл биелэгдэхгүй болтол үргэлжлэнэ. fractal хээн дээр жишээ авч үзье:
float octaves = 4;
float amp = 1;
varying point pp = p;
float lacunarity = 2;
float gain = 0.5;
float i;
varying float sum = 0, fw = filterwidthp(P);
for (i = 0; i < octaves; i += 1)
{
sum += amp * filteredsnoise (pp, fw);
amp *= gain; pp *= lacunarity; fw *= lacunarity;
}
RSL дээр цикл гүйцэтгэх нөгөө арга нь while бүтэц. Бүтцийн хувьд FOR циклтэй тун адилхан. Гол ялгаа нь булын илэрхийлэл нь заавал loop statement агуулсан байх шаардлагагүй. Ийм учир болгоомжтой хэрэглэхгүй бол хязгааргүй циклдэж зурагчыг гацаах аюул дагуулдаг. WHILE циклийг бичих дүрмийг харцгаая.
while (булын илэрхийлэл)
{
код.....;
}
Өмнөх for циклэд ашигласан жишээг while ашиглаж хэрхэн хийхийг харцгаая.
float octaves = 4;
float amp = 1;
varying point pp = p;
float lacunarity = 2;
float gain = 0.5;
float i = 0; // анхны утгыг цикл эхлэхийн өмнө онооно
varying float sum = 0, fw = filterwidthp(P);
while(i < octaves)
{
sum += amp * filteredsnoise (pp, fw);
amp *= gain; pp *= lacunarity; fw *= lacunarity;
i += 1; // индексиийг зайлшгүй өөрчлөх хэрэгтэй. Эс бөгөөс хязгааргүй циклд өртөнө.
}
For циклээс ялгарах гол ялгаанууд нь анхны утга оноох болон нөхцөл шалгах хувьсагчийг байнга өөрчлөх явдал. Булын илэрхийлэлд ашиглагдаж байгаа хувьсагчийн утгыг нэмэгдүүлэхгүй бол (i < octaves) илэрхийлэл нь байнга үнэн утга буцааж зурагч тасралтгүй ажиллана. Тиймээс хэрэглэгч энэ процессыг гараар зогсоохоос нааш төгсөхгүй. Дээр үзсэн урсгалыг хянах гурван бүтэц гурвуулаа нэгээс их мөртэй код агуулсан бол { } хаалтанд хийх шаардлага тавьдаг. Нэг мөрөнд багтах бол хаалтанд хаших хэрэгцээгүй.
if ( нөхцөл) {
илэрхийлэл1;
}
[else {
илэрхийлэл2;
}]
Нөхцөл шалгах үйлдэлд ifelse-if гинжин холбоосыг ашиглаж болно. else-if нөхцлийг ашигласан тохиолдолд төгсгөлийг үргэлж else-гээр хийх хэрэгтэй. Аль ч “if” нь заасан шаардлага биелэхгүй бол төгсгөлийн else дээр очно.
if (нөхцөл1){
илэрхийлэл1;
}
else if (нөхцөл2) {
илэрхийлэл2;
}
else {
илэрхийлэл3;
}
If дотор байрлах нөхцөл нь ихэвчлэн Булын илэрхийлэл байдаг. RSL нь булын төрлийг дэмждэггүй боловч булын төрөл дээр хийгддэг үйлдлүүдийг гүйцэтгэж чадна. Өөрөөр хэлбэл 1 (үнэн ) ба 0 (худал) гэсэн утга буцаадаг үйлдлүүд юм. Нөхцөл шалгахад зөвхөн харьцуулах операторуудыг ашиглана гэдэг нь хамгийн чухал дүрэм юм. Өөрөөр хэлбэл С хэл шиг нөхцөл шалгах бололцоогүй:
if (1 - 0) z = z * 2; // алдаатай
Учир нь булын илэрхийлэл байх ёстой газар float илэрхийлэл ашигласан. Түүнчлэн өнгө, цэг гэх мэтийг агуулсан илэрхийлэл байхыг зөвшөөрдөггүй. Энэ дүрэм эсрэгээрээ ч хүчинтэй. Бутархай тоо, өнгө, цэг зэрэг өгөгдлүүдэд 0 эсвэл 1 гэсэн утга буцаадаг булын илэрхийлэл ашиглан утга оноох боломжгүй. Булын илэрхийлэлд ашигладаг харьцуулах операторуудыг харцгаая:
Тэмдэгт Нэр Нөхцөл биелвэл 1-ийг буцаана
== тэнцүү Зүүн тал нь баруунтайгаа тэнцүү бол
!= тэнцүү биш Зүүн тал нь баруунтайгаа тэнцүү биш бол
< бага Зүүн тал нь баруунаасаа бага бол
<= бага буюу тэнцүү Зүүн тал нь баруунаасаа бага болон тэнцүү бол
> их Зүүн тал нь баруунаасаа их бол
>= их буюу тэнцүү Зүүн тал нь баруунаасаа их болон тэнцүү бол
Адил төрлийн өгөгдлийг харьцуулахад ямар ч асуудал байхгүй. Харин өөр төрлийн өгөгдлүүдийг харьцуулахдаа дараахи зүйлсийг анхаарах хэрэгтэй:
■Бутархай ганц өгөгдлийг гурван элемент бүхий өгөгдөлтэй харьцуулахад энэ нь гурван элемент тус бүр дээр гүйцэтгэгдэнэ.
■ Цэгийг өнгөтэй харьцуулж болохгүй.
■ Хоёр матрицыг зөвхөн == ба =! операторын туслалцаатай харьцуулна.
Хамгийн түгээмэл ашиглагддаг if хэллэгийн жишээг харъя. Шэйдрт зураг, текстур оноосон эсэхийг урьдчилан шалгана. Хэрвээ хоосон бол цулгуй өнгө онооно.
varying color myColor;
if (myTexture != "")
{
myColor = texture(myTexture,s,t);
}
else
{
myColor= color (1,1,1);
}
Заримдаа нэгээс олон хэд хэдэн нөхцлийг зэрэг шалгах хэрэгцээ гардаг. Энэ тохиолдолд && (ба) , the || (эсвэл) гэсэн 2 операторыг ашиглана. && оператор нь үйлдлийг зүүнээс баруун тийш чиглэлтэй биелүүлэх бөгөөд энэ гинжин доторхи нэг нөхцөл биелэгдэхгүй бол else-рүү шилжинэ.
color baseColor = color (0.5,0.5,0.5);
if ((myTexture != "") && (textureValue > 0))
{
textureCol = texture (myTexture,s,t);
colOut = mix (baseColor,textureCol,textureValue);
}
else if ((myTexture == "") && (textureValue > 0))
{
colOut = mix (baseColor, color(1,0,0), textureValue);
}
Дээрхи жишээнд үзүүлснээр текстур нь хоосон биш бөгөөд textureValue нь 0-ээс их байвал myTexture-ийн утгыг baseColor-той холино. Эхний нөхцөл биелвэл дараагийнх руу шилжих замаар ажиллана. Эхний нөхцөл хангагдахгүй бол хяналт нь дараагийн нөхцлийг алгасаж шууд else блок руу шилжинэ. myTexture нь хоосон боловч textureValue нь тэгээс их бол baseColor-ийг улаан өнгөтэй холих үйлдэл хийгдэнэ.
if ((Ks <= 0) || (specColor == (0,0,0))
{
Cspec = color (0,0,0);
}
else {
Cspec = specular(V,Nf,roughness) * Ks * specColor;
}
Дээрхи жишээнд харуулснаар гялаан гэрлийн коэффицент (ихэвчлэн Ks) эсвэл гялаан гэрлийн өнгийн (specColor) аль нэг нь 0 буюу хар бол specular тооцооллыг орхигдуулна. || операторыг ашиглаж байхад эхний нөхцөл биелвэл дараагийн нөхцлийг шалгах шаардлага байхгүй. Операторын логикууд болон үр дүнг тооцоолох, ямар нөхцлийг ашиглавал хамгийн оновчтой шийдэл болох зэргийг сурахад бага зэргийн хүчин чармайлт гаргах хэрэгтэй. Оператор бүр хоорондоо ялгаатай бөгөөд үзүүлэх үр дүн ч адил биш.
if ( myValue >0 ) нөхцөл нь if (myValue >= 0) нөхцлөөс ялгаатай.
RSL нь С хэлэнд ашиглагддаг if-else-ийн хялбаршуулсан хэлбэрийг ашиглахыг зөвшөөрдөг. if else ашиглан тун богино команд гүйцэтгэх бол энэ арга нь тун тохиромжтой. Доорхи жишээнд текстур оноохоос өмнө текстур координатын s , t-ийн утгыг нөгөө тийш нь хөрвүүлэх шаардлагатай үгүйг тодорхойлно. Бичих дүрэм нь ( нөхцөл ? илэрхийлэл 1: илэрхийлэл 2) хэлбэртэй байна. Жишээ:
uniform float flips = 0;
uniform float flipt = 0;
varying float ss = (flips == 0 ? s: 1 - s);
varying float tt = (flipt == 0 ? t: 1 - t);
color textureColor = texture ("mytex.tx",ss,tt);
if-else -ийн талаар анхааруулах хамгийн сүүлийн зүйл нь бусад програмчлалын хэлэнд өргөн хэрэглэгддэг доорхи маягийн бүтэц.
if (хувьсагчийнНэр) {үйлдэлүүд .... }
Бусад хэлний сompiler нь хувьсагч NULL болон 0-ээс өөр утгатай бол нөхцөл биелнэ гэж үздэг. Харин RSL-д ингэж бичвэл алдаа заана. Тэгэхээр бутархай эсвэл тэмдэгт мөр үү гэдгээс шалтгаалан дараахи маягаар бичих хэрэгтэй.
if (хувьсагчийнНэр != 0) эсвэл if (хувьсагчийнНэр != “”)
Шэйдр дотор байж болох урсгалыг хянах өөр нэг хэрэгсэл бол for, while итераци бүтцүүд юм. Энэ командууд нь текст илэрхийллийг үндэслэн бүлэг код сегментийг дахин давтан гүйцэтгэхэд хэрэглэгддэг. Хоорондоо төстэй их хэмжээний код агуулсан шэйдр үүсгэхэд эдгээр командууд маш их хэрэгтэй. Жишээ fractal хээ үүсгэдэг шэйдр хийлээ гэж бодъё. Үүний тулд хэд хэдэн өөр өөр хэмжээтэй noise давхаргуудыг цикл ашиглан үүсгэж сүүлд нь нэг текстур болгон нэгтгэнэ. FOR цикл нь дараахи синтаксийг баримтална.
for (initialization, булын илэрхийлэл , loop statement)
{
код.....;
}
initialization гэдэг нь цикл дөнгөж эхлэхэд хувьсагчид анхны утга оноох үйлдэл . Дараа нь булын илэрхийллийн нөхцлийг шалгана. . Хэрэв нөхцөл биелвэл доторхи кодыг гүйцэтгэнэ. Кодыг гүйцэтгэж дууссаны дараа loop statement-ийг гүйцэтгэнэ. Loop statement нь ихэвчлэн цикл эхлэхэд оноосон хувьсагчийн утгыг өөрчлөх илэрхийлэл байдаг. Loop statement-ийг гүйцэтгэсний дараа boolean expression-д байрлах нөхцлийг ахин шалгаад үнэн бол кодыг давтан гүйцэтгэнэ. Энэ процесс нөхцөлт илэрхийлэл биелэгдэхгүй болтол үргэлжлэнэ. fractal хээн дээр жишээ авч үзье:
float octaves = 4;
float amp = 1;
varying point pp = p;
float lacunarity = 2;
float gain = 0.5;
float i;
varying float sum = 0, fw = filterwidthp(P);
for (i = 0; i < octaves; i += 1)
{
sum += amp * filteredsnoise (pp, fw);
amp *= gain; pp *= lacunarity; fw *= lacunarity;
}
RSL дээр цикл гүйцэтгэх нөгөө арга нь while бүтэц. Бүтцийн хувьд FOR циклтэй тун адилхан. Гол ялгаа нь булын илэрхийлэл нь заавал loop statement агуулсан байх шаардлагагүй. Ийм учир болгоомжтой хэрэглэхгүй бол хязгааргүй циклдэж зурагчыг гацаах аюул дагуулдаг. WHILE циклийг бичих дүрмийг харцгаая.
while (булын илэрхийлэл)
{
код.....;
}
Өмнөх for циклэд ашигласан жишээг while ашиглаж хэрхэн хийхийг харцгаая.
float octaves = 4;
float amp = 1;
varying point pp = p;
float lacunarity = 2;
float gain = 0.5;
float i = 0; // анхны утгыг цикл эхлэхийн өмнө онооно
varying float sum = 0, fw = filterwidthp(P);
while(i < octaves)
{
sum += amp * filteredsnoise (pp, fw);
amp *= gain; pp *= lacunarity; fw *= lacunarity;
i += 1; // индексиийг зайлшгүй өөрчлөх хэрэгтэй. Эс бөгөөс хязгааргүй циклд өртөнө.
}
For циклээс ялгарах гол ялгаанууд нь анхны утга оноох болон нөхцөл шалгах хувьсагчийг байнга өөрчлөх явдал. Булын илэрхийлэлд ашиглагдаж байгаа хувьсагчийн утгыг нэмэгдүүлэхгүй бол (i < octaves) илэрхийлэл нь байнга үнэн утга буцааж зурагч тасралтгүй ажиллана. Тиймээс хэрэглэгч энэ процессыг гараар зогсоохоос нааш төгсөхгүй. Дээр үзсэн урсгалыг хянах гурван бүтэц гурвуулаа нэгээс их мөртэй код агуулсан бол { } хаалтанд хийх шаардлага тавьдаг. Нэг мөрөнд багтах бол хаалтанд хаших хэрэгцээгүй.
No comments:
Post a Comment