forked from IndieVisualLab/UnityGraphicsProgrammingBook1
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathaoyama.re
643 lines (433 loc) · 24.7 KB
/
aoyama.re
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
= Grow grass with geometry shader
== Introduction
In this chapter, we focus on the Geometry Shader, which is one of the stages of the rendering pipeline, and explain the dynamic grass generation shader using the Geometry Shader (so-called Grass Shader).
I'm using some jargon to describe the Geometry Shader, but if you want to use the Geometry Shader for now, it's quick to take a look at the sample code.
The Unity project in this chapter has been uploaded to the following Github repository.
@<href>{https://github.com/IndieVisualLab/UnityGraphicsProgramming/, https://github.com/IndieVisualLab/UnityGraphicsProgramming/}
== Geometry Shader What is
Geometry Shader Is a programmable shader that can dynamically convert, create, and delete primitives (basic shapes that make up a mesh) on the GPU.
Up to now, when trying to change the mesh shape dynamically, such as converting primitives, it is necessary to perform processing on the CPU, or to add meta information to the vertices in advance and convert with Vertex Shader. did.
However, Vertex Shader could not get information about adjacent vertices, and there was a strong constraint that new vertices could not be created based on the vertices being processed or vice versa. ..
Also, even if that is done by CPU, it takes an unrealistically huge amount of time from the viewpoint of real-time processing.
As described above, there have been some problems with changing the shape of the mesh in real time.
Therefore, as a function to solve these problems and allow conversion processing freely under weak constraints, Geometry Shader is standardly installed in DirectX 10 and OpenGL 3.2.
In OpenGL, it is also called the Primitive Shader.
== Geometry Shader Features of
=== Rendering pipeline
On the rendering pipeline Vertex Shader Next to Fragment Shader It is located before the rasterization process.
In other words, in the Fragment Shader, it is processed without distinguishing between the vertices dynamically generated by the Geometry Shader and the original vertices passed to the Vertex Shader.
=== Geometry Shader Input to
Normally, the input information to the Vertex Shader is in units of vertices, and the conversion process for that vertex is performed. However, the input information to the Geometry Shader will be the primitive unit for input defined by the user.
The actual program is described later, but the vertex information group processed by Vertex Shader will be divided and input based on the input primitive type.
For example, if the input primitive type is triangle, three vertex information will be passed, if it is line, two vertex information will be passed, and if it is point, one vertex information will be passed.
This makes it possible to perform processing while referring to other vertex information, which was not possible with the vertex shader, and it becomes possible to perform a wide range of calculations.
One point to note is that the Vertex Shader performs processing on a per-vertex basis, and information about the vertices to be processed is passed. Processing is performed in units of the primitive determined by.
In other words, when Geometry Shader is executed on a Quad mesh whose topology is Triangles as shown in Figure 6.1, Geometry Shader is executed twice for triangles ① and ②.
At this time, if the input primitive type is Line, the information passed to the input is the triangle of ①, two vertices of vertices 0, 1 and 2, and ② of vertices of 0, 2, 3 It is the apex of two points.
//image[aoyama/img0][Quad mesh][scale=0.4]
=== Geometry Shader Output from
Geometry Shader The output of is the vertex information group for the user-defined output primitive type. Vertex Shader With 1 input and 1 output, the Geometry Shader outputs multiple pieces of information, and there is no problem even if there is more than one primitive generated by the output information.
For example, if you define the output primitive type as triangle and output a total of 9 vertices newly calculated, 3 triangles are generated by the Geometry Shader. Since this processing is performed in primitive units as described above, it is possible that the number of triangles that was originally one has increased to three.
Also, Geometry Shader For MaxVertexCount, it is necessary to set in advance the maximum number of vertices to be output in one process.
For example, if MaxVertexCount is set to 9, the Geometry Shader will be able to output the number of vertices from 0 to 9.
This number is generally the maximum value of 1024 due to the "Geometry Shader limit" described later.
It is important to note that when outputting vertex information, when adding a new vertex while maintaining the original mesh shape, the vertex information sent from the Vertex Shader should also be added to the Geometry Shader. Should be output.
The Geometry Shader is not the behavior of adding to the output of the Vertex Shader, but the output of the Geometry Shader is rasterized and passed to the Fragment Shader.
Paradoxically, you can also reduce the number of vertices dynamically by setting the output of the Geometry Shader to 0.
=== Geometry Shader Limits
The Geometry Shader has the limitation of the maximum number of output vertices and the maximum number of output elements for one output.
The maximum number of output vertices is literally the limit value of the number of vertices, and although it is a numerical value that depends on the GPU, 1024 etc. is common, so you can increase the number of vertices from one triangle to a maximum of 1024 points.
The elements in the maximum number of output elements are the information that the vertices such as coordinates and colors have, and generally the position element of (x, y, z, w) and (r, g, b, a) There are a total of 8 color elements. The maximum output number of this element also depends on the GPU, but 1024 is also common, so the output will be limited to 128 (1024/8) at the maximum.
Since these two restrictions need to be satisfied, even if it is possible to output 1024 points in terms of the number of vertices, due to the constraint on the number of elements side, the actual Geometry Shader The output of is limited to 128 points.
So, for example, if you use Geometry Shader for a mesh with 2 primitives (Quad mesh etc.), you can only handle vertices up to 256 points (128 points * 2 primitives). ..
This number of 128 points is the limit value of the value that can be set in MaxVertexCount in the previous section.
== Easy Geometry Shader
Below is a simple Geometry Shader program.
The explanation up to the previous section will be explained again with reference to the actual program.
In addition to the Geometry Shader, the explanation of the ShaderLab syntax required when writing shaders in Unity is omitted in this chapter, so if you have any questions, please refer to the official document below.
@<href>{https://docs.unity3d.com/ja/current/Manual/SL-Reference.html,https://docs.unity3d.com/ja/current/Manual/SL-Reference.html}
//emlist[][cs]{
Shader "Custom/SimpleGeometryShader"
{
Properties
{
_Height("Height", float) = 5.0
_TopColor("Top Color", Color) = (0.0, 0.0, 1.0, 1.0)
_BottomColor("Bottom Color", Color) = (1.0, 0.0, 0.0, 1.0)
}
SubShader
{
Tags { "RenderType" = "Opaque"}
LOD 100
Cull Off
Lighting Off
Pass
{
CGPROGRAM
#pragma target 5.0
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#include "UnityCG.cginc"
uniform float _Height;
uniform float4 _TopColor, _BottomColor;
struct v2g
{
float4 pos : SV_POSITION;
};
struct g2f
{
float4 pos : SV_POSITION;
float4 col : COLOR;
};
v2g vert(appdata_full v)
{
v2g o;
o.pos = v.vertex;
return o;
}
[maxvertexcount(12)]
void geom(triangle v2g input[3],
inout TriangleStream<g2f> outStream)
{
float4 p0 = input[0].pos;
float4 p1 = input[1].pos;
float4 p2 = input[2].pos;
float4 c = float4(0.0f, 0.0f, -_Height, 1.0f)
+ (p0 + p1 + p2) * 0.33333f;
g2f out0;
out0.pos = UnityObjectToClipPos(p0);
out0.col = _BottomColor;
g2f out1;
out1.pos = UnityObjectToClipPos(p1);
out1.col = _BottomColor;
g2f out2;
out2.pos = UnityObjectToClipPos(p2);
out2.col = _BottomColor;
g2f o;
o.pos = UnityObjectToClipPos(c);
o.col = _TopColor;
// bottom
outStream.Append(out0);
outStream.Append(out1);
outStream.Append(out2);
outStream.RestartStrip();
// sides
outStream.Append(out0);
outStream.Append(out1);
outStream.Append(o);
outStream.RestartStrip();
outStream.Append(out1);
outStream.Append(out2);
outStream.Append(o);
outStream.RestartStrip();
outStream.Append(out2);
outStream.Append(out0);
outStream.Append(o);
outStream.RestartStrip();
}
float4 frag(g2f i) : COLOR
{
return i.col;
}
ENDCG
}
}
}
//}
In this shader, the center coordinates of the passed triangle are calculated, moved further upward, and each vertex of the passed triangle is connected to the new calculated coordinates.
In other words, you are generating a simple triangular pyramid from a flat triangle.
So when you apply this shader to a Quad mesh (consisting of two triangles), you get something like Figure 6.2 to Figure 6.3.
//image[aoyama/img1][From a flat plate like this][scale=0.4]
//image[aoyama/img2][Two three-dimensional pyramids will be displayed][scale=0.4]
Of this shader, I will extract and explain only the part related to Geometry Shader.
//emlist[][cs]{
#pragma target 5.0
#pragma vertex vert
// Geometry Shader Declare use of
#pragma geometry geom
#pragma fragment frag
#include "UnityCG.cginc"
//}
In the declaration section above, @<tt>{geom}I declare that the function named is for Geometry Shader.
By doing this, when it comes to the Geometry Shader stage@<tt>{geom}The function will be called.
//emlist[][cs]{
[maxvertexcount(12)]
void geom(triangle v2g input[3], inout TriangleStream<g2f> outStream)
//}
Here is the function declaration for the Geometry Shader.
=== input
//emlist[][cs]{
triangle v2f input[3]
//}
This is the part related to input.
Since I want to generate a triangular pyramid from a triangle this time, input is@<tt>{triangle}I am.
As a result, each vertex information of the triangle which is a unit primitive is input, and since the triangle consists of three vertices, the received dummy argument is an array of length 3.
So if you type@<tt>{triangle}not@<tt>{point}If you set to, there is only one vertex to configure, so@<tt>{geom(point v2f input[1])}You will receive it as an array of length 1 like.
=== output
//emlist[][cs]{
inout TriangleStream<g2f> outStream
//}
This is the output part.
Since we want to make the primitive of the mesh generated this time a triangle,@<tt>{TriangleStream} It is declared with a type.
@<tt>{TriangleStrema}Since the type means that the output is a triangle strip, it will generate a triangle based on the output vertex information.
Other@<tt>{PointStream} Type@<tt>{LineStream}Since there are types etc., it is necessary to select the output primitive type according to the purpose.
Also,@<tt>{[maxvertexcount(12)]}The maximum output number is set to 12 in the part.
This is because the number of triangles that make up the triangular pyramid is one on the bottom and three on the side, and there are a total of four vertices per triangle, so 3 * 4 outputs 12 vertices. It is set to 12 because it will be different.
=== processing
//emlist[][cs]{
g2f out0;
out0.pos = UnityObjectToClipPos(p0);
out0.col = _BottomColor;
g2f out1;
out1.pos = UnityObjectToClipPos(p1);
out1.col = _BottomColor;
g2f out2;
out2.pos = UnityObjectToClipPos(p2);
out2.col = _BottomColor;
g2f o;
o.pos = UnityObjectToClipPos(c);
o.col = _TopColor;
// bottom
outStream.Append(out0);
outStream.Append(out1);
outStream.Append(out2);
outStream.RestartStrip();
// sides
outStream.Append(out0);
outStream.Append(out1);
outStream.Append(o);
outStream.RestartStrip();
outStream.Append(out1);
outStream.Append(out2);
outStream.Append(o);
outStream.RestartStrip();
outStream.Append(out2);
outStream.Append(out0);
outStream.Append(o);
outStream.RestartStrip();
//}
This is the part of the process that outputs the actual vertices.
First of all, g2f type variable for output is declared and the vertex coordinates and color information are stored.
At this time, it is necessary to convert from the object space to the camera clip space in the same way as the Vertex Shader.
After that, the vertex information is output while being aware of the order of the vertices that make up the mesh.
@<tt>{outStream}Variable@<tt>{Append}By passing an output variable to the function, it will be added to the current stream,@<tt>{RestartStrip}Calling the function ends the current primitive strip and starts a new stream.
this is,@<tt>{TriangleStream}Is a triangle strip, so@<tt>{Append}As more vertices are added by the function, multiple connected triangles will be created based on all vertices added to the stream.
So, like this time, triangles@<tt>{Append}If you have trouble connecting based on the order@<tt>{RestartStrip}You need to call to start a new stream.
of course@<tt>{Append}By devising the order@<tt>{RestartStrip}It is possible to reduce the number of function calls.
== Grass Shader
This section explains the Grass Shader that uses the Geometry Shader to generate grass in real time, which is a slight development of the "Simple Geometry Shader" in the previous section.
The following is the program of Grass Shader to be described.
//emlist[][cs]{
Shader "Custom/Grass" {
Properties
{
// Grass height
_Height("Height", float) = 80
// Width of grass
_Width("Width", float) = 2.5
// Height of bottom of grass
_BottomHeight("Bottom Height", float) = 0.3
// Height of middle part of grass
_MiddleHeight("Middle Height", float) = 0.4
// Top of grass
_TopHeight("Top Height", float) = 0.5
// Width of bottom of grass
_BottomWidth("Bottom Width", float) = 0.5
// Width of middle part of grass
_MiddleWidth("Middle Width", float) = 0.4
// Width of the top of the grass
_TopWidth("Top Width", float) = 0.2
// Bending of the bottom of the grass
_BottomBend("Bottom Bend", float) = 1.0
// Bending of the middle part of the grass
_MiddleBend("Middle Bend", float) = 1.0
// Bending of the upper part of the grass
_TopBend("Top Bend", float) = 2.0
// Wind strength
_WindPower("Wind Power", float) = 1.0
// Color of the top of the grass
_TopColor("Top Color", Color) = (1.0, 1.0, 1.0, 1.0)
// Color of the bottom of the grass
_BottomColor("Bottom Color", Color) = (0.0, 0.0, 0.0, 1.0)
// A noise texture that gives randomness to the height of the grass
_HeightMap("Height Map", 2D) = "white"
// Noise texture that gives randomness to the direction of grass
_RotationMap("Rotation Map", 2D) = "black"
// Noise texture that gives randomness to wind strength
_WindMap("Wind Map", 2D) = "black"
}
SubShader
{
Tags{ "RenderType" = "Opaque" }
LOD 100
Cull Off
Pass
{
CGPROGRAM
#pragma target 5.0
#include "UnityCG.cginc"
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
float _Height, _Width;
float _BottomHeight, _MiddleHeight, _TopHeight;
float _BottomWidth, _MiddleWidth, _TopWidth;
float _BottomBend, _MiddleBend, _TopBend;
float _WindPower;
float4 _TopColor, _BottomColor;
sampler2D _HeightMap, _RotationMap, _WindMap;
struct v2g
{
float4 pos : SV_POSITION;
float3 nor : NORMAL;
float4 hei : TEXCOORD0;
float4 rot : TEXCOORD1;
float4 wind : TEXCOORD2;
};
struct g2f
{
float4 pos : SV_POSITION;
float4 color : COLOR;
};
v2g vert(appdata_full v)
{
v2g o;
float4 uv = float4(v.texcoord.xy, 0.0f, 0.0f);
o.pos = v.vertex;
o.nor = v.normal;
o.hei = tex2Dlod(_HeightMap, uv);
o.rot = tex2Dlod(_RotationMap, uv);
o.wind = tex2Dlod(_WindMap, uv);
return o;
}
[maxvertexcount(7)]
void geom(triangle v2g i[3], inout TriangleStream<g2f> stream)
{
float4 p0 = i[0].pos;
float4 p1 = i[1].pos;
float4 p2 = i[2].pos;
float3 n0 = i[0].nor;
float3 n1 = i[1].nor;
float3 n2 = i[2].nor;
float height = (i[0].hei.r + i[1].hei.r + i[2].hei.r) / 3.0f;
float rot = (i[0].rot.r + i[1].rot.r + i[2].rot.r) / 3.0f;
float wind = (i[0].wind.r + i[1].wind.r + i[2].wind.r) / 3.0f;
float4 center = ((p0 + p1 + p2) / 3.0f);
float4 normal = float4(((n0 + n1 + n2) / 3.0f).xyz, 1.0f);
float bottomHeight = height * _Height * _BottomHeight;
float middleHeight = height * _Height * _MiddleHeight;
float topHeight = height * _Height * _TopHeight;
float bottomWidth = _Width * _BottomWidth;
float middleWidth = _Width * _MiddleWidth;
float topWidth = _Width * _TopWidth;
rot = rot - 0.5f;
float4 dir = float4(normalize((p2 - p0) * rot).xyz, 1.0f);
g2f o[7];
// Bottom.
o[0].pos = center - dir * bottomWidth;
o[0].color = _BottomColor;
o[1].pos = center + dir * bottomWidth;
o[1].color = _BottomColor;
// Bottom to Middle.
o[2].pos = center - dir * middleWidth + normal * bottomHeight;
o[2].color = lerp(_BottomColor, _TopColor, 0.33333f);
o[3].pos = center + dir * middleWidth + normal * bottomHeight;
o[3].color = lerp(_BottomColor, _TopColor, 0.33333f);
// Middle to Top.
o[4].pos = o[3].pos - dir * topWidth + normal * middleHeight;
o[4].color = lerp(_BottomColor, _TopColor, 0.66666f);
o[5].pos = o[3].pos + dir * topWidth + normal * middleHeight;
o[5].color = lerp(_BottomColor, _TopColor, 0.66666f);
// Top.
o[6].pos = o[5].pos + dir * topWidth + normal * topHeight;
o[6].color = _TopColor;
// Bend.
dir = float4(1.0f, 0.0f, 0.0f, 1.0f);
o[2].pos += dir
* (_WindPower * wind * _BottomBend)
* sin(_Time);
o[3].pos += dir
* (_WindPower * wind * _BottomBend)
* sin(_Time);
o[4].pos += dir
* (_WindPower * wind * _MiddleBend)
* sin(_Time);
o[5].pos += dir
* (_WindPower * wind * _MiddleBend)
* sin(_Time);
o[6].pos += dir
* (_WindPower * wind * _TopBend)
* sin(_Time);
[unroll]
for (int i = 0; i < 7; i++) {
o[i].pos = UnityObjectToClipPos(o[i].pos);
stream.Append(o[i]);
}
}
float4 frag(g2f i) : COLOR
{
return i.color;
}
ENDCG
}
}
}
//}
When this shader is applied to a Plane mesh with multiple rows and columns, it becomes as shown in Figure 6.4.
//image[aoyama/img3][Grass Shaderの結果][scale=0.6]
I will explain the process of generating grass from this.
=== Basic policy
This time, we will generate one grass for each primitive.
Regarding the generation of the grass shape, as shown in Figure 6.5, a total of 7 vertices are generated by dividing into the lower part, the middle part, and the upper part. I will.
//image[aoyama/img4][How to make a grass shape][scale=0.4]
=== parameter
Although the details are described in the comments, the coefficient that controls the width and height of each part (lower part, middle part, upper part) in one grass, the coefficient that controls the width and height of the entire grass It is prepared as the main parameter.
Also, it doesn't look good if each grass has the same shape, so use a noise texture to give it randomness.
=== processing
//emlist[][cs]{
float height = (i[0].hei.r + i[1].hei.r + i[2].hei.r) / 3.0f;
float rot = (i[0].rot.r + i[1].rot.r + i[2].rot.r) / 3.0f;
float wind = (i[0].wind.r + i[1].wind.r + i[2].wind.r) / 3.0f;
float4 center = ((p0 + p1 + p2) / 3.0f);
float4 normal = float4(((n0 + n1 + n2) / 3.0f).xyz, 1.0f);
//}
In this part, we calculate the height and direction of the grass, and the numerical value that serves as the standard for the strength of the wind.
It may be calculated in the Geometry Shader, but it is possible to calculate it with the Vertex Shader because it is possible to handle it like the initial value when performing calculation on the Geometry Shader if the vertex has meta-information. I am.
//emlist[][cs]{
float4 center = ((p0 + p1 + p2) / 3.0f);
float4 normal = float4(((n0 + n1 + n2) / 3.0f).xyz, 1.0f);
//}
Here, the center of the grass and the direction of growing grass are calculated.
If you decide the part here with noise texture, you can give randomness to the direction in which the grass grows.
//emlist[][cs]{
float bottomHeight = height * _Height * _BottomHeight;
...
o[6].pos += dir * (_WindPower * wind * _TopBend) * sin(_Time);
//}
The program is abbreviated because it is long.
In this part, the height and width of the lower part, middle part and upper part are calculated respectively, and the coordinates are calculated based on that.
//emlist[][cs]{
[unroll]
for (int i = 0; i < 7; i++) {
o[i].pos = UnityObjectToClipPos(o[i].pos);
stream.Append(o[i]);
}
//}
The 7 vertices calculated in this part@<tt>{Append}doing.
This time it doesn't matter if the triangles are connected and generated, so@<tt>{RestartStrip}I haven't.
In addition,@<tt>{for}For statements@<tt>{[unroll]}Is applied.
This is an attribute that expands the processing in the loop by the number of loops at compile time, and it has the disadvantage of increasing the memory size, but it has the advantage of operating at high speed.
== Summary
So far, I have explained from the description of Geometry Shader to the basic and applied programs.
There are some differences in characteristics from writing a program that runs on a CPU, but it should be possible to use it by suppressing the basic parts.
In general, it is said that Geometry Shader is slow.
I myself haven't felt that much, but it may be difficult when the scope of use becomes large.
If you think you will be using the Geometry Shader on a large scale, please try benchmarking once.
Still, I think that being able to dynamically create new meshes and delete them on the GPU will broaden the range of ideas.
Personally, I think the most important thing is not what technology was used but what is created and expressed by it.
We hope you will learn and learn about a tool called Geometry Shader in this chapter, and feel new possibilities.
== reference
* Tutorial 13: Geometry Shader - @<href>{https://msdn.microsoft.com/ja-jp/library/bb172497,https://msdn.microsoft.com/ja-jp/library/bb172497}
* Geometry shader object in MSDN - @<href>{https://msdn.microsoft.com/ja-jp/library/ee418313,https://msdn.microsoft.com/ja-jp/library/ee418313}
* Rendering Method for Transparent Geometry by Cutting Geometry Shader Geometry - @<href>{http://t-pot.com/program/147_CGGONG2008/index.html,http://t-pot.com/program/147_CGGONG2008/index.html}