Chapters 3, 4 Unchecked

pull/393/head
nikos-maximus 1 year ago
parent 1ecb87820b
commit 96b68b7e87

@ -0,0 +1,61 @@
## Uniforms
Μέχρι τώρα έχουμε δει πως η GPU διαχειρίζεται μεγάλο πλήθος από παράλληλα threads (νήματα), όπου το καθένα είναι υπεύθυνο να αναθέσει σε κάθε τμήμα της τελικής εικόνας το χρώμα του. Αν και κάθε παράλληλο thread είναι τυφλό προς τα υπόλοιπα, χρειάζεται να μπορούμε να στείλουμε κάποιες εισόδους από τη CPU (ΚΜΕ) σε όλα τα threads. Εξ' αιτίας της αρχιτεκτονικής της κάρτας γραφικών, αυτές οι έισοδοι θα είναι ίδιες (uniform - ομοιόμορφες) για όλα τα threads, και κατ' ανάγκη είναι *μόνο για ανάγνωση (read only)*. Με άλλα λόγια, κάθε thread παίρνει τα ίδια δεδομένα τα οποία μπορεί να διαβάσει αλλά όχι και να αλλάξει.
Αυτές οι είσοδοι λέγονται `uniform` και υπάρχουν για τους περισσότερους από τους υποστηριζόμενους τύπους: `float`, `vec2`, `vec3`, `vec4`, `mat2`, `mat3`, `mat4`, `sampler2D` και `samplerCube`. Τα uniforms ορίζονται μαζί με τον αντίστοιχο τύπο στην κορυφή του shader, αμέσως μετά την ανάθεση της ακρίβειας κινητής υποδιαστολής (default floating point precision).
```glsl
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution; // Μέγεθος καμβά (πλάτος, ύψος)
uniform vec2 u_mouse; // θέση του mouse σε συντεταγμένες οθόνης (σε pixels)
uniform float u_time; // Χρόνος σε δευτερόλεπτα (seconds) από τη φόρτωση της σελίδας
```
Μπορούμε να φανταστούμε τα uniforms σαν μικρές γέφυρες ανάμεσα στην CPU και τη GPU. Τα ομόματα θα διαφέρουν από υλοποίηση σε υλοποίηση, αλλά σε αυτά τα παραδείγματα πάντα τα περνάω σαν: `u_time` (χρόνος σε δευτερόλεπτα από τη στιγμή που ξεκίνησε ο shader), `u_resolution` (μέγεθος του πίνακα όπου ζωγραφίζεται ο shader) και `u_mouse` (θέση του mouse μέσα στον πίνακα σε pixels). Ακολουθώ τη σύμβαση να προσθέτω `u_` πριν από το όνομα του uniform ώστε να είμαι σαφής ως προς τη φύση της μεταβλητής, αλλά θα συναντήσετε όλων των ειδών τα ονόματα για uniforms. Για παράδειγμα, το [ShaderToy.com](https://www.shadertoy.com/) χρησιμοποιεί τα ίδια uniforms αλλά με τα ακόλουθα ονόματα:
```glsl
uniform vec3 iResolution; // ανάλυση του viewport (περιοχή "θέασης") (σε pixels)
uniform vec4 iMouse; // συντεταγμένες του mouse σε pixel. xy: τρέχουσα θέση, zw: θέση click
uniform float iTime; // χρόνος εκτέλεσης (playback) του shader (σε seconds)
```
Αλλά αρκετά με τα λόγια, ας δούμε τα uniforms στην πράξη. Στον ακόλουθο κώδικα χρησιμοποιούμε το `u_time` - τον αριθμό δευτερολέπτων από τη στιγμή που ο shaders άρχισε να εκτελείται - σε συνδυασμό με μια ημιτονοειδή συνάρτηση για να δώσουμε κίνηση στη μεταβολή του κόκκινου στον πίνακα.
<div class="codeAndCanvas" data="time.frag"></div>
Όπως βλέπετε, η GLSL έχει κι άλλες εκπλήξεις. Η GPU υποστηρίζει σε hardware συναρτήσεις γωνίας, τριγωνομετρικές και εκθετικές. Μερικές από αυτές τις συναρτήσεις είναι: [`sin()`](../glossary/?search=sin), [`cos()`](../glossary/?search=cos), [`tan()`](../glossary/?search=tan), [`asin()`](../glossary/?search=asin), [`acos()`](../glossary/?search=acos), [`atan()`](../glossary/?search=atan), [`pow()`](../glossary/?search=pow), [`exp()`](../glossary/?search=exp), [`log()`](../glossary/?search=log), [`sqrt()`](../glossary/?search=sqrt), [`abs()`](../glossary/?search=abs), [`sign()`](../glossary/?search=sign), [`floor()`](../glossary/?search=floor), [`ceil()`](../glossary/?search=ceil), [`fract()`](../glossary/?search=fract), [`mod()`](../glossary/?search=mod), [`min()`](../glossary/?search=min), [`max()`](../glossary/?search=max) και [`clamp()`](../glossary/?search=clamp).
Ώρα να παίξουμε πάλι με τον παραπάνω κώδικα.
* Επιβραδύνετε τη συχνότητα μέχρι η αλλαγή χρώματος σχεδόν να μη γίνεται αντιληπτή.
* Επιταχύνετέ τη μέχρι να βλέπετε ένα ενιαίο χρώμα που δε θα τρεμοσβήνει (flicker).
* Παίξτε με τα τρία κανάλια (RGB) σε διαφορετικές συχνότητες για να πάρετε ενδιαφέροντες συνδυασμούς και συμπεριφορές.
## gl_FragCoord
Κατά τον ίδιο τρόπο που η GLSL μας δίνει μια προκαθορισμένη (default) έξοδο, `vec4 gl_FragColor`, μας δίνει και μια προκαθορισμένη είσοδο, `vec4 gl_FragCoord`, η οποία περιέχει τις συντεταγμένες οθόνης του *pixel* ή *τεμάχιου οθόνης - screen fragment* στο οποίο δουλεύει το ενεργό thread. Με το `vec4 gl_FragCoord`, ξέρουμε που δουλεύει ένα thread μέσα στον πίνακα. Σε αυτή την περίπτωση δεν το ονομάζουμε `uniform` γιατί θα είναι διαφορετικό από το ένα thread στο άλλο, αντίθετα, το `gl_FragCoord` λέγεται *varying*.
<div class="codeAndCanvas" data="space.frag"></div>
Στον παραπάνω κώδικα *κανονικοποιούμε (normalize)* τις συντεταγμένες του fragment (τεμαχίου) διαιρώντας το με τη συνολική ανάλυση του πίνακα. Με αυτό τον τρόπο, οι τιμές θα βρίσκονται μεταξύ `0.0` και `1.0`, κάτι που το κάνει εύκολο να αντιστοιχίσουμε τις τιμές X και Y στα κανάλια RED (κόκκινο) και GREEN (πράσινο).
Στον κόσμο των shaders, δεν έχουμε και πολλές δυνατότητες για debugging (διόρθωση σφαλμάτων) πέρα από το να αναθέτουμε έντονα χρώματα σε μεταβλητές και να προσπαθούμε να βγάλουμε άκρη από αυτά. Θα ανακαλύψετε πως κάποιες φορές το να προγραμματίζετε σε GLSL είναι πολύ παρόμοιο με το να φτιάχνει κανείς καραβάκια μέσα σε μπουκάλια. Είναι εξ' ίσου δύσκολο, όμορφο και ευχάριστο.
![](08.png)
Ώρα να δοκιμάσουμε να τσεκάρουμε πόσο έχουμε κατανοήσει αυτό τον κώδικα.
* Μπορείτε να βρείτε που στον καμβά μας βρίσκονται οι συντεταγμένες `(0.0, 0.0)`;
* Ή οι συντεταγμένες `(1.0, 0.0)`, `(0.0, 1.0)`, `(0.5, 0.5)` και `(1.0, 1.0)`;
* Μπορείτε να βρείτε πως να χρησιμοποιήσετε το `u_mouse` γνωρίζοντας πως οι τιμές είναι σε pixels και ΟΧΙ κανονικοποιημένες; Μπορείτε να το χρησιμοποιήσετε για να κινήστε τα χρώματα;
* Μπορείτε να επινοήσετε έναν ενδιαφέροντα τρόπο να αλλάζετε το χρωματικό συνδυασμό χρησιμοποιώντας τις συντεταγμένες `u_time` και `u_mouse`?
Αφού ολοκληρώσετε αυτές τις ασκήσεις, θα αναρωτηθείτε ίσως πού αλλού μπορείτε να χρησιμοποιήστε τις νέες σας shaderοδυνάμεις. Στο επόμενο κεφάλαιο θα δούμε πως να φτιάξετε τα δικά σας εργαλεία shaders σε three.js, Processing, και openFrameworks.

@ -0,0 +1,233 @@
## Εκτελώντας τον shaders σας
Σαν μέρος της σύνθεσης αυτού του βιβλίου και της εξάσκησής μου στην πράξη, έφτιαξα ένα οικοσύστημα από εργαλεία ώστε που μπορεί κανείς να δημιουργήσει, να εμφανίσει, να μοιραστεί και να επιμεληθεί τους shaders του. Τα εργαλεία αυτά λειτουργούν ομοιόμορφα σε Linux, MacOS, Windows και [Raspberry Pi](https://www.raspberrypi.org/) και browsers (φυλλομετρητές) χωρίς να χρειάζεται να αλλάξετε τον κώδικά σας.
## Εκτελώντας τους shaders σας σε browser
**Εμφάνιση**: όλα τα διαδραστικά παραδείγματα σε αυτό το βιβλίο απεικονίζονται χρησιμοποιώντας [glslCanvas](https://github.com/patriciogonzalezvivo/glslCanvas) το οποίο κάνει τη διαδικασία του να τρέξουμε αυτόνομους shaders εξαιρετικά εύκολη.
```html
<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
```
Όπως βλέπετε, χρειάζεται μόνο ένα `canvas` (HTML) element με `class="glslCanvas"` και το url του shader στο `data-fragment-url`. Μάθετε περισσότερα [εδώ](https://github.com/patriciogonzalezvivo/glslCanvas).
Αν είστε σαν εμένα, πιθανόν θα θέλετε να τρέξετε shaders απευθείας από τη γραμμή εντολών, σε αυτή την περίπτωση προτείνω να δείτε το [glslViewer](https://github.com/patriciogonzalezvivo/glslViewer). Αυτή η εφαρμογή σας επιτρέπει να ενσωματώσετε shaders στα scripts (σενάρια εκτέλεσης) σας φλοιού `bash` ή σε pipelines ("σωληνώσεις" εργαλείων) unix και να τα χρησιμοποιήσετε παρόμοια με το [ImageMagick](http://www.imagemagick.org/script/index.php). Επίσης, το [glslViewer](https://github.com/patriciogonzalezvivo/glslViewer) είναι ένας πολύ καλός τρόπος να μεταγλωττίζετε shaders στο [Raspberry Pi](https://www.raspberrypi.org/) σας, ο οποίος είναι και ο λόγος που το [openFrame.io](http://openframe.io/) το χρησιμοποιεί για να εμφανίσει έργα τέχνης με shaders. Βρείτε περισσότερα για αυτή την εφαρμογή [εδώ](https://github.com/patriciogonzalezvivo/glslViewer).
```bash
glslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
```
**Δημιουργία**: προκειμένου να αναδείξω την εμπειρία του να γράφουμε shaders, έφτιαξα ένα online editor (πρόγραμμα επεξεργασίας κειμένου) με όνομα [glslEditor](https://github.com/patriciogonzalezvivo/glslEditor). Αυτός ο editor είναι ενσωματωμένος στα διαδραστικά παραδείγματα του βιβλίου. Μας δίνει ένα σύνολο από χρήσιμα widgets (εργαλεία διεπαφής) ώστε να κάνει πιο "απτή" την αφηρημένη εμπειρία του να δουλεύουμε με κώδικα glsl. Μπορείτε επίσης να τον τρέξετε σαν αυτόνομη εφαρμογή web από εδώ [editor.thebookofshaders.com/](http://editor.thebookofshaders.com/). Μάθετε περισσότερα σχετικά [εδώ](https://github.com/patriciogonzalezvivo/glslEditor).
![](glslEditor-01.gif)
Αν προτιμάτε να δουλεύετε offline με το [SublimeText](https://www.sublimetext.com/) μπορείτε να εγκαταστήσετε αυτό το [πακέτο για τον glslViewer](https://packagecontrol.io/packages/glslViewer). Μάθετε περισσότερα [εδώ](https://github.com/patriciogonzalezvivo/sublime-glslViewer).
![](glslViewer.gif)
**Διανομή**: με τον online editor ([editor.thebookofshaders.com/](http://editor.thebookofshaders.com/)) μπορείτε να μοιραστείτε τους shaders σας! Και η online και ενσωματωμένη και η ανεξάρτητη έκδοση έχουν ένα κουμπί export (εξαγωγή) με το οποίο μπορείτε να πάρετε ένα μοναδικό URL για τον shader σας. Επίσης έχει τη δυνατότητα να κάνει export απευθείας σε ένα [openFrame.io](http://openframe.io/).
![](glslEditor-00.gif)
**Επιμέλεια**: Το να μοιραστείτε τον κώδικά σας είναι το πρώτο βήμα προς το να μοιραστείτε τον shader σας σαν έργο τέχνης! Εκτός από την επιλογή να κάνετε export σε [openFrame.io](http://openframe.io/) έφτιαξα ένα εργαλείο με το οποίο μπορείτε να οργανώσετε τους shaders σας σε μια έκθεση που μπορεί να ενσωματωθεί σε οποιοδήποτε site (ιστοχώρο), το όνομά του είναι [glslGallery](https://github.com/patriciogonzalezvivo/glslGallery). Δείτε περισσότερα [εδώ](https://github.com/patriciogonzalezvivo/glslGallery).
![](glslGallery.gif)
## Εκτελώντας τους shaders στο περιβάλλον της προτίμησής σας
Αν έχετε ήδη εμπειρία προγραμματισμού σε κάποιο περιβάλλον όπως: [Processing](https://processing.org/), [Three.js](http://threejs.org/), [OpenFrameworks](http://openframeworks.cc/) ή [SFML](https://www.sfml-dev.org/), θα ανυπομονείτε πιθανόν να δοκιμάσετε shaders στις πλατφόρμες με τις οποίες αισθάνεστε οικεία. Τα παρακάτω είναι παραδείγματα για το πως να ορίσετε shaders σε κάποια γνωστά περιβάλλοντα με τα ίδια uniforms τα οποία θα χρησιμοποιήσουμε σε όλο το βιβλίο. (Στο [GitHub repository αυτού του κεφαλαίου](https://github.com/patriciogonzalezvivo/thebookofshaders/tree/master/04), θα βρείτε ολόκληρο τον κώδικα γι' αυτά τα τρία περιβάλλοντα.)
### Σε **Three.js**
Ο ευφυέστατος και πολύ σεμνός Ricardo Cabello (επίσης γνωστός και σαν [MrDoob](https://twitter.com/mrdoob) ) αναπτύσσει ανάμεσα σε άλλους [συνεργάτες](https://github.com/mrdoob/three.js/graphs/contributors) ένα πιθανόν από τα πιο γνωστά περιβάλλοντα για WebGL, που ονομάζεται [Three.js](http://threejs.org/). Θα βρείτε πολλά παραδείγματα, μαθήματα και βιβλία που διδάσκουν πως να χρησιμοποιήσετε αυτή τη βιβλιοθήκη JavaScript για να δημιουργήσετε φοβερά 3D γραφικά.
Ακολουθεί ένα παράδειγμα για τις HTML και JS που που χρειάζεστε για να ξεκινήσετε με shaders σε three.js. Δώστε ιδιαίτερη σημασία στο script `id="fragmentShader"`, εδώ είναι που μπορείτε να αντιγράψετε τους shaders που βρίσκετε σε αυτό το βοβλίο.
```html
<body>
<div id="container"></div>
<script src="js/three.min.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
void main() {
gl_Position = vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 u_resolution;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
gl_FragColor=vec4(st.x,st.y,0.0,1.0);
}
</script>
<script>
var container;
var camera, scene, renderer, clock;
var uniforms;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.Camera();
camera.position.z = 1;
scene = new THREE.Scene();
clock = new THREE.Clock();
var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
uniforms = {
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new THREE.Vector2() },
u_mouse: { type: "v2", value: new THREE.Vector2() }
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
container.appendChild( renderer.domElement );
onWindowResize();
window.addEventListener( 'resize', onWindowResize, false );
document.onmousemove = function(e){
uniforms.u_mouse.value.x = e.pageX
uniforms.u_mouse.value.y = e.pageY
}
}
function onWindowResize( event ) {
renderer.setSize( window.innerWidth, window.innerHeight );
uniforms.u_resolution.value.x = renderer.domElement.width;
uniforms.u_resolution.value.y = renderer.domElement.height;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
uniforms.u_time.value += clock.getDelta();
renderer.render( scene, camera );
}
</script>
</body>
```
### Σε **Processing**
Η [Processing](https://processing.org/) η οποία πρωτοξεκίνησε από τους [Ben Fry](http://benfry.com/) και [Casey Reas](http://reas.com/) το 2001, είναι ένα εξαιρετικά απλό και ισχυρό περιβάλλον για να κάνετε τα πρώτα σας βήματα σε κώδικα (σίγουρα ήταν για 'μένα). Ο [Andres Colubri](https://codeanticode.wordpress.com/) έχει κάνει σπουδαίες ενημερώσεις για την OpenGL και το video στην Processing, καθιστώντας το εκολότερο από ποτέ να παίξει κανείς με GLSL shaders στο φιλικό αυτό περιβάλλον. Η Processing ψάχνει για τον shader με όνομα `"shader.frag"` στον φάκελο `data` του sketch ("σκίτσο" - το project της Processing). Αντιγράψτε τα παραδείγματα που βρίσκετε εδώ σε αυτό τον φάκελο, και μετονομάστε το αρχείο.
```cpp
PShader shader;
void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
```
Προκειμένου να δουλέψει ο shader σε εκδόσεις παλιότερες της 2.1, πρέπει να προσθέσετε την παρακάτω γραμμή στην αρχή του shader: `#define PROCESSING_COLOR_SHADER`, έτσι ώστε να δείχνει ως εξής:
```glsl
#ifdef GL_ES
precision mediump float;
#endif
#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
```
Για περισσότερες πληροφορίες σχετικά με τους shaders σε Processing, δείτε αυτό το [μάθημα](https://processing.org/tutorials/pshader/).
### Σε **openFrameworks**
Για τον καθένα υπάρχει ένα μέρος όπου νιώθει άνετα, στην περίπτωσή μου, εξακολουθεί να είναι η [κοινότητα openFrameworks](http://openframeworks.cc/). Αυτή η βιβλιοθήκη C++ παρέχει ένα πλαίσιο αφαίρεσης γύρω απο την OpenGL και άλλες βιβλιοθήκες C++ ανοιχτού κώδικα. Κατά πολλές έννοιες είναι παρόμοια με την Processing, αλλά με τις προφανείς επιπλοκές του να δουλεύει κανείς με μεταγλωττιστές C++. Όμοια με την Processing, η openFrameworks αναζητά τα αρχεία shader στο φάκελο data, οπότε μην παραλείψετε να αντιγράψετε τα αρχεία `.frag` που θέλετε να χρησιμοποιήσετε και να αλλάξετε το όνομά τους όταν τα φορτώνετε.
```cpp
void ofApp::draw(){
ofShader shader;
shader.load("","shader.frag");
shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
```
Αν θέλετε να χρησιμοποιήσετε το πλήρες σύνολο απο uniforms που υπάρχουν στον ορισμό των GlslViewer και GlslCanvas με απλούστερο τρόπο σε OpenFrameworks, συνιστώ το addon (πρόσθετο) [ofxShader](https://github.com/patriciogonzalezvivo/ofxshader) το οποίο επίσης υποστηρίζει πολλαπλούς buffers (μνήμη εικόνας), shaders υλικών, hotreload (άμεση επανεκτέλεση) και αυτόματη μετατροπή σε OpenGL ES για το Raspberry Pi. Και ο κώδικάς σας θα απαιτεί μόνο το παρακάτω:
```cpp
//--------------------------------------------------------------
void ofApp::setup(){
ofDisableArbTex();
sandbox.allocate(ofGetWidth(), ofGetHeight());
sandbox.load("grayscott.frag");
}
//--------------------------------------------------------------
void ofApp::draw(){
sandbox.render();
sandbox.draw(0, 0);
}
```
Για περισσότερες πληροφορίες για τους shaders σε openFrameworks δείτε αυτό το [εξαιρετικό μάθημα](http://openframeworks.cc/ofBook/chapters/shaders.html) από τον [Joshua Noble](http://thefactoryfactory.com/).
### Σε **Blender**
Το [GlslTexture](https://github.com/patriciogonzalezvivo/glslTexture) είναι ένα addon που επιτρέπει να παράξετε textures (υφές) προγραμματιστικά χρησιμοποιώντας GLSL Shaders, και είναι πλήρως συμβατό με τα υπόλοιπα απο τα sandboxes (περιβάλλοντα) αυτού του κεφαλαίου. Να πως λειτουργεί:
1. Αναζήτηση Operator: `F3``SpaceBar (διάστημα)` ανάλογα με τις επιλογές περιβάλλοντος του Blender). Γράψτε `GlslTexture`
![](blender/00.png)
2. Αλλάξτε τα μεγέθη `width` και `height` και το αρχείο `Source` (όπου μπορείτε να βάλετε το path -τοποθεσία- ενός εξωτερικού αρχείου).
![](blender/01.png)
3. Χρησιμοποιήστε την Εικόνα στα υλικά σας. Το όνομα της Εικόνας βασίζεται στο όνομα του αρχείου source (βλ. 2)
![](blender/02.png)
4. Πηγαίνετε στον Text Editor (επεξεργαστή κειμένου - ή έναν εξωτερικό επεξεργαστή κειμένου αν το source αρχείο είναι εξωτερικό) και αλλάξτε τον shader. Οι αλλαγές είναι άμεσα ορατές.
![](blender/03.png)
Loading…
Cancel
Save