[持續更新]先進OpenGL編程注意事項


來源請注明http://www.cnblogs.com/vertexshader/articles/3022981.html,復制不可恥,復制不加原文地址最可恥。

GLSL

1.Compatibility Build-in Variable:在AMD的驅動中和Nvidia的驅動中對Compatibility Build-in Variables的態度是不同的,在AMD的驅動上使用Comatibility Build-in Variables將不會產生任何的問題,而在Nvidia的驅動中編譯帶有Compatibility Build-in Variables的GLSL代碼段會導致Warning的出現。抱歉一個Warning也不許有!是時候拋棄任何Compatibility Build-in Variables and functions了。[GLSL][08/01/2014]

2.Type Safe:在AMD的驅動中對類型安全似乎沒有那么嚴格的要求,而在Nvidia中對類型安全有嚴格的要求,同樣的代碼段在兩者的設備上會產生不同的編譯結果:

float factor;
if(factor)
{
//...
}

在AMD上這段代碼將毫無問題的編譯通過,很靠近C/C++的風格和習慣,而在Nvidia上將拋異常“error C7011: implicit cast from float to bool”,很明顯地反應了對於類型安全要求的不同。而根據規范《OpenGL Shading Language 4.4》的描述:

The OpenGL Shading Language is type safe. There are some implicit conversions between types. Exactly how and when this can occur is described in section 4.1.10 “Implicit Conversions” and as referenced by other sections in this specification.

也就是說GLSL語言是類型安全的,但是存在部分類型之間可以進行隱式轉換,具體的類型轉換由下表表示:


 
由此可以見得AMD的驅動是不符合規范的,Nvidia的驅動符合這一規范的。[GLSL][08/01/2014]

3.Bindless Texture:Bindless Texture會使得OpenGL 4.2提供的Opaque-Uniform Layout Qualifiers失效,類似layout(binding = integer-constant)中的<integer-constant>指的是Texture Unit,而並非是Bindless handle,但是可以通過explicit uniform location指定uniform location的值,然后外部可以通過這個location的值,通過glUniformHandle*上傳相關紋理的handle。出現這個擴展后,紋理分為了bindless和bound類型,而在沒有啟用bindless之前,所有的紋理默認就是bound類型的,也就是過去通過Texture Unit然后通過glUniform1i上傳的。[OpenGL 4.4][12/11/2013]

4.Vertex Attrib:  Vertex Program的輸出限定在gl_MaxOutputComponent的四分之一,超過這個數值,是實現定義的,可能產生錯誤。

The number of input locations available to a shader is limited. For vertex shaders, the limit is the advertised number of vertex attributes. For all other shaders, the limit is implementation-dependent and must be no less than one fourth of the advertised maximum input component count.A program will fail to link if any attached shader uses a location greater than or equal to the number of supported locations, unless device-dependent optimizations are able to make the program fit within available hardware resources. 

5.Array: GLSL允許首先聲明數組,然后在使用之前的時候聲明其長度,如果不聲明其長度,索引其的時候肯定會產生錯誤:

#version 440

uniform vec4 pos[];

const int length = 5;
uniform vec4 pos[length];

 

 

 

OpenGL 4

 1.Bindless Texture:過去OpenGL進行采樣需要將Texture Object綁定到Texture Unit,然后通過glUniform1i上傳Texture Unit的值;現在有了Bindless Texture,Shader可以直接通過Handle訪問Texture Object,也沒有了Texture Unit數量的限制,可以更快訪問紋理和綁定更多的紋理(over 1 million unique textures!)。[OpenGL 4.4][09/11/2013]

2.Immutable Storage:OpenGL通過GL_ARB_texture_storage和GL_ARB_buffer_storage來支持新的存儲就是Immutable Storage,和字面的意思一樣是不可改變大小的存儲。Texture Object增加了GL_TEXTURE_IMMUTABLE_FORMAT來標識是否是Immutable storage;Buffer Object增加了GL_BUFFER_IMMUTABLE_STORAGE來標識是否是Immutable Storage——其實這些也非常有好處,在實時渲染中需要動態地改變資源的大小的情況很少。glTexStorage*提供的好處就是優化了創建紋理的過程,可以通過這個API直接創建Mipmap層而不需要調用glGenerateMipmap函數,而且能避免設置Texture Level所產生的隱藏錯誤;也不需要拆分internalformat為format和type,直接提供internalformat就可以。glBufferStorage和glBufferData的區別就是提供了更多的精細控制:GL_DYNAMIC_STORAGE_BIT提供了開啟/關閉Client端更新Server端的功能;GL_MAP_READ_BIT和GL_MAP_WRITE_BIT提供了CPU讀取/寫入數據的功能;GL_MAP_PERSISTENT_BIT提供了當數據仍然在Mapped狀態時也能夠被讀取/寫入數據的功能;GL_MAP_COHERENT_BIT提供了服務端數據和客戶端數據的一致性(這個有待探討);而GL_CLIENT_STORAGE_BIT提供了顯示指定數據存儲在客戶端的功能,如果這個bit沒設置,那么數據默認存儲在服務端。這些API與D3D的API基本是類似的,例如D3D中通過D3D11Device::CreateBuffer創建的Buffer Object也是Immutable Storage,想要Resize Buffer則需要重新調用這個函數創建一個新的Buffer Object然后獲取新的句柄,這個和OpenGL的函數是一致的。

這些函數和TexImage*的區別就是,TexStorage*聲明后創建的Storage的Format和大小都是不可變的,故在之后調用TexImage*、CopyTexImage*、CompressTexImage*和TexStorage*都是會報錯且無效的;並且創建的Storage總是Mipmap Complete(對於TextureCube還有Cubemap Complete)的,這是Immutable Texture Storage主要解決的問題。總之,Immutable Texture Storage是可以替代Mutable Texture Storage的,並且更加高效。[OpenGL 4.4][01/16/2014]

3.Separate Program Object:擴展GL_ARB_separate_shader_object帶來了新的性能提升,過去通過編譯Shader Object,然后綁定Shader Object到Shader Program上然后Link完成Shader的載入,體現出來的問題就是許多相同的shader需要重復地Link,降低了程序的效率。這個擴展則基本上弱化了Shader Object的功能,使用Shader Program作為Shader Stage,可以把文本的Shader通過glCreateShaderProgramv來直接創建(其實等價於創建一個Shader Object,編譯后綁定到Shader Program上,然后再Link),或者先glCreateProgram創建好Shader Program,然后通過glProgramBinary上傳編譯好的Shader(這個過程也會Link程序)。這個擴展還提供了一個新的Container Object就是Program Pipeline Object,相當於VAO一樣是用來記錄使用了哪些Shader Stage,之前通過glUseProgramStages設定好需要使用的Program Stage,然后綁定它到Context就激活了使用的Program;同時當Program Pipeline Object未綁定的時候,也可以使用原來的Program Object來開啟可編程功能,不過顯而易見沒理由不全部使用Program Pipeline Object了。Shader Stage現在和D3D是類似的了,Program Pipeline Object在D3D中需要自己弄一個,其實底層也就是設置D3D11context:*SetShader,功能上基本一致,抽象在一起很簡單。

這是一個OpenGL 4帶來的重大改變之一。過去使用Shader必須先創建Shader Object,編譯好之后綁定在一個Shader Program上面,鏈接完畢之后使用glUseProgram激活相應的Shader Program,這么做導致了幾個問題:①同一個Shader Object可能綁定到多個Shader Program上,不同的Shader Program之間都可能會有相同的Shader 代碼,導致重復鏈接;②更換Shader Program,可能會導致Vertex Array Object也需要更換。GL_EXT_separate_shader_objects擴展的升級版本GL_ARB_separate_shader_objects帶來了Separate Shader Objects,允許OpenGL像Direct3D一樣將Program單獨分成好幾個Stage,更改其中的一個Stage並不影響其他的Stage,這樣可以有更好的靈活性。

這個擴展也帶來繁瑣的ProgramUniform*函數系列(其實這些函數來自DSA擴展),用來上傳Uniform數據,和調用ActiveShaderProgram之后然后調用Uniform*函數系列一樣,這些函數對於不同類型都有不同的函數,頻繁調用會造成很大的性能問題,而且最后這些函數上傳的值,最后也會放入Default Uniform Buffer Object里,不推薦使用,應該用更加高效的Uniform Buffer Object替代,用更加MapBuffer來更新Uniform Buffer Object。另外談到的一點就是,在啟用Separate Shader Objects的時候,每個Stage的Shader Program都帶有獨立的namespace,而不是綁定到Program Pipeline Object的所有Shader Program都共享一個namespace,那么這樣不同的Shader Program里的Uniform變量可以擁有相同的名字。[OpenGL 4.1][01/16/2014]

 4.BPTCOpenGL現在也完全支持了D3D中的所有的壓縮紋理,加上自己的ETC,壓縮的格式挺多的,不過還是推薦使用BC,用的比較多。[OpenGL 4.2][01/16/2014]

 5.Shader Binary:擴展GL_ARB_get_program_binary提供了不需要在運行態編譯shader的方法——之前編譯好並保存為二進制文件,通過glGetProgramBinary可以下載編譯好的二進制數據,通過glProgramBinary可以上傳編譯好的二進制數據,這樣可以大大提升程序的性能。這個和D3D也是一樣的,可以事先把Shader編譯進一個Blob中,序列化進入二進制文件,使用的時候直接加載即可。[OpenGL 4.1][01/16/2014]

6.Texture  View:除了創建的時候簡潔的代碼和能避免一般錯誤之外,Immutable Texture Storage還有一項好處就是Immutable Storage可以被其他的Texture Object共享,而Mutable Storage只能綁定在單獨的Texture Object上。擴展GL_ARB_texture_view實現了共享Immutable Texture Storage的函數TextureView,在功能上有點類似Texture Copy,但是本質上是為了減少內存拷貝,使不同的Texture Object能訪問一塊內存區域。[OpenGL 4.3][01/16/2014]

7.一維壓縮紋理:很奇怪的是,s3tc定義的內容是,如果想要創建一個一維的壓縮紋理,則會產生GL錯誤,結果到了glTexStorage這里則不發生錯誤,成功開辟了空間,也許是一個BUG。[OpenGL 4.3][01/27/2014]

8.Cube Map & Cube Map Array:這個用法非常的Tricky,對於GL_TEXTURE_CUBE_MAP使用的是glTexStorage2D,GL會創建六個面的數據;而對於GL_TEXTURE_CUBE_MAP_ARRAY,卻要用glTexStorage3D來創建,並且depth的值要設置為6N,而不是N。一個是自己創建六個面,一個是讓你指定六個面,這接口設計的~ [OpenGL 4.3][03/04/2014]

9.Tricky的指針:在使用Buffer Object之后,OpenGL中很多的函數比如glDrawElements或者glGetTexImage最后的一個參數再也不是一個內存指針,而是檢測當前綁定點綁定的buffer object然后直接使用null,或者當成一個byte offset來使用,有很多人嘗試使用100* 10這樣或者是GLvoid*(100)來強制轉換,這明顯是錯誤的!既然是一個byte offset,傳入的自然也是一個指針:

GLbyte *offset = 0;
offst += 100;

// Has bind a PBO to GL_PIXEL_PACK_BUFFER
glGetTexImage(target, level, format, type, offset);

是不是一個很Tricky的設計![OpenGL 4.0][03/04/2014]

 

OpenGL 3

OpenGL Operations

1.Transform feedback: glTransformFeedbackVaryings只是一次有效的,多次調用glTransformFeedbackVaryings只有最后一次是有效的。故GL_INTERLEAVED_ATTRIB與GL_SEPARATE_ATTRIB只能存在一個而不能同時存在,這比Direct3D中Streamout可以通過綁定slot和SOTarget的辦法要tricky的多,但是D3D中RenderLayout也只是設置一次。[OpenGL 3.3][04/15/2013]

2.Shader Program: 當glUseProgram的參數為0時,那么當前的shader program是無效的,並且其輸出結果是未定義的。雖然如此,但是OpenGL不會認為其是一個錯誤。[OpenGL 3.3][04/16/2013]

3.Binding Buffer Objects to Indexed Targets: glBindBufferOffset存在於諸多Registry中,但並未收錄在Spec和Reference中,不過其等價於glBindBufferRange(target, index, buffer, offset, buffer_size - offset),和D3D中SOSetTarget相同。[OpenGL 3.3][04/20/2013]

4.Interleaved & Separate: Transform Feedback支持2種緩沖區模式,但是其實本質是一樣的,GL_MAX_TRANSFORM_FEEDBACK_ATTRIB限制了輸出變量的數量,變量最大的為matrix4x4,也就是16 components,GL_MAX_TRANSFORM_FEEDBACK_ATTRIB * 16 = GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,本質上只是將輸出變量路由到不同的Stream Out Target(buffer)而已。所以最好的方法就是只綁定一個Stream Out Target(buffer),這樣可以讓Direct3D的SOSetTarget和OpenGL的BindBufferOffset行為一致,更易於封裝。[OpenGL 3.3][04/20/2013]

5.Vertex Shader Input: 雖然GLSL內部支持float,int,bool三個類型的變量,但是實質上允許輸入的變量和glVertexAttribPointer支持的類型有關。[OpenGL 3.3][04/24/2013] 

6.Shader Program: Direct3D並沒有Shader Program Object,要激活相同功能,必須通過Set函數來一一設置Shader Object對象。[OpenGL 3.3][05/01/2013] 

7.Vertex Attrib Pointer: 你不得不使用Vertex Array Object!如果單獨設置glVertexAttribPointer,每次Dall Call的時候這個狀態就要重新設置一遍,否則就會出現隱含的錯誤。[OpenGL 3.3][05/05/2013]

8.Shader Object: 如果一個Shader Object沒有Attach到任何Shader Program,那么Delete它的操作是立即的;但是當它已經Attach到Shader Program上的時候,Delete操作不是立即的,而是先做一個flag,當它不再被Attach的的時候Delete。一個好的建議是,可以利用智能指針自動析構Shader Object對象,減少內存而不拋出異常。[OpenGL 3.3][05/06/2013]

Rasterization

 1.Point Size: 可以通過Shader內建變量gl_PointSize來設置(但是要啟用GL_PROGRAM_POINT_SIZE),而不通過光柵化狀態glPointSize,這和Direct3D中Semantic設置為PSIZE的Buffer相同。[OpenGL 3.3][04/16/2013]

2.Texture Completeness: 一個導致紋理不起作用的行為是,當你創建了一個紋理而未顯式指定GL_TEXTURE_MIN_FILTER的時候,默認的值是GL_NEAST_MIPMAP_LINEAR,而默認的GL_TEXTURE_MAX_LEVEL是1000。[OpenGL 3.3][05/01/2013]

3.SMOOTH: 值得注意的是,在舊有的Rasterizer state中,GL_XXX_SMOOTH這類光柵化狀態已經無明顯作用,想要啟用AA應該使用Multisample,或者其他的方式。[OpenGL 3.3][05/01/2013] 

4.Pixel Format: 適用於RenderBuffer和Texture的格式:

– RGBA32F, RGBA32I, RGBA32UI, RGBA16, RGBA16F, RGBA16I, RGBA16UI, RGBA8, RGBA8I, RGBA8UI, SRGB8_ALPHA8, RGB10_A2, and RGB10_A2UI.
– R11F_G11F_B10F.
– RG32F, RG32I, RG32UI, RG16, RG16F, RG16I, RG16UI, RG8, RG8I, and RG8UI.
– R32F, R32I, R32UI, R16F, R16I, R16UI, R16, R8, R8I, and R8UI.

只適用於Texture的格式:

– RGBA16_SNORM and RGBA8_SNORM.
– RGB32F, RGB32I, and RGB32UI.
– RGB16_SNORM, RGB16F, RGB16I, RGB16UI, and RGB16.
– RGB8_SNORM, RGB8, RGB8I, RGB8UI, and SRGB8.
– RGB9_E5.

– RG16_SNORM, RG8_SNORM, COMPRESSED_RG_RGTC2 and COMPRESSED_SIGNED_RG_RGTC2.
– R16_SNORM, R8_SNORM, COMPRESSED_RED_RGTC1 and COMPRESSED_SIGNED_RED_RGTC1.[OpenGL 3.3][05/03/2013]

5.Mipmap: 雖然OpenGL可以使用glGenerateMipMap自動生成MipMap層,但是生成MipMap的過程比預先加載做好的MipMap要慢,會產生可能存在的性能問題。[OpenGL 3.3][05/23/2013]

6. Texture map operation: OpenGL的Texture不支持map操作,需要借助pixel buffer object來完成。一個值得注意的問題是,當你需要從Texture中download數據的時候,glGetTexImage這個函數將會返回一個level的所有數據,這里需要調整。

Per-fragment Operation

1.Depth Buffer Test: 在OpenGL中,Depth Buffer Test是默認關閉的,需要自行開啟glEnable(GL_DEPTH_TEST),比較Tricky。[OpenGL3.3][11/18/2013]

Whole Framebuffer Operation

1.Render Buffer Storage: 重復對一個RBO對象調用glRenderbufferStorage或者glRenderbufferStorageMultisample,將會銷毀原有的storage創建新的storage——但是“極其”不推薦這樣做,如果需要一個新的RBO,刪除它然后再次創建。重復創建RBO可能導致完整性問題,尤其是當它正綁定在其他對象上的時候。

"Similar to glTexImage2D​, calling this function on a renderbuffer that has already had this function called on it will cause it to deallocate any resources associated with the previous call and allocate new storage. Note: You are strongly advised not to do this. If you need a new renderbuffer, just delete the old object and create a new one. Recreating a renderbuffer with the same object name can cause completeness problems, particularly if it is attached to another object at the time." [OpenGL 3.3][04/20/2013]

2.Render Target: Render Target在OpenGL中可以有兩種,即Render Buffer Object和Texture,操作都比較相同;同時都支持反走樣,即使用glRenderbufferStorageMultisample和glTexImage{2,3}DMultisample來開啟;將其attach到Frame Buffer Object的方法類似,使用glFramebufferRenderbuffer,glFramebuffer,glFramebufferTexture{1,2,3}D還有glFramebufferLayer來完成。

Several types of framebuffer-attachable images: ① The image of a renderbuffer object, which is always two-dimentional; ② A single level of a one-dimentional texture, which is treated as a two dimensional image with a height of one; ③ A single level of a two-demensional or rectangle texture; ④ A single face of a cube map texture level, which is treated as two-dimensional image; ⑤ A single layer of a one-or two-dimentional array texture or three-dimensional texture, which is treaded as a two-dimensional image.

這些類型與Direct3D的D3D10_RENDER_TARGET_VIEW_DESC和D3D11_RENDER_TARGET_VIEW_DESC中允許的Buffer, Texture1D, Texture1DArray, Texture2D, Texture2DArray, Texture2DMS, Texture2DMSArray, Texture3D一致,也完全可以封裝上層類執行相同的過程。另外同樣這些類型都支持Pixel Transfer Operation,Render Buffer Object需要通過glReadBuffer指定,然后通過glReadPixel完成Pixel Transfer。[OpenGL 3.3][04/20/2013]

3.Clear: OpenGL的Clear操作分為glClear,glClearColor,glClearDepth,glClearStencil和glClearBuffer*,與IDirect3DDevice9::Clear類似;到了Direct3D10和Direct3D11, Clear操作被細分為ClearRenderTargetView和ClearDepthStencilView。OpenGL的函數可以輕松匹配這些,使用glClearBuffer{i,f,ui}和glClearBufferfi即可,而glClearColor、glClearDepth與glClearStencil可以做次要考慮。[OpenGL 3.3][04/22/2013]

4.Render State: OpenGL本身沒有Render State,Direct3D中的Render State很多都是對Whole Framebuffer Operation中的Fine Control of Buffer Update的簡單封裝,完全可以自己封裝一個Render State用於OpenGL中。[OpenGL 3.3][04/23/2013]

5.Framebuffer Object: Framebuffer不占用多少內存,它只是一個 狀態向量對象(State Vector Object)。當你綁定Framebuffer的時候,驅動需要消耗CPU time來驗證Framebuffer的狀態,這就產生了性能上的問題,所以最好還是只有一個Framebuffer Object用來做Render to Texture。Direct3D沒有Framebuffer Object,Render Target足以完成工作,事實上OMSetRenderTarget已經完成了FBO的工作。Framebuffer在一定程度上方便了Pixel Transfer操作,但是增加了性能問題。

"Is it better to make 1 FBO and bind your texture to it each time you need to render to the texture?"
"An FBO itself doesn't use much memory. It is a state vector object. In terms of performance, each time you bind, the driver needs to validate the state which costs CPU time. Logically, it would be better to have 1 FBO per Render_To_Texture (RTT).However, it has been found that you get a speed boost if your textures is the same size and you use 1 FBO for them.If you have 10 textures that are 64x64 and 10 textures that are 512x64, make 2 FBOs. One FBO for each group."[OpenGL 3.3][04/23/2013]

6.Fragment Output Route: GLSL中Fragment Output Route通過對out變量指定layout綁定slot,通過glDrawBuffer路由到Render Targets(不推薦使用compatibility的gl_FragData[]),而Depth/Stencil通過gl_FragDepth輸出;同樣,HLSL通過SV_Target[]直接輸出到對應的RenderTargetView,而Depth/Stencil通過SV_Depth輸出到DepthStencilView。一個較好的方法是,犧牲OpenGL的靈活性,glDrawBuffers的buf參數從COLOR_ATTACHMENT0~COLOR_ATTACHMENTi按照數量遍歷一遍,這樣可以使行為一致更易於封裝。[OpenGL 3.3][04/24/2013]

7.RenderBuffer: Renderbuffer的用途是無格式的buffer用來存儲fragement shader輸出,例如可以存儲depth和stencil,其本質是一個2D的Image,其大小GL_MAX_RENDERBUFFER_SIZE和GL_MAX_TEXTURE_SIZE相同證明了這點。但是其靈活性較差,不能直接被PBO通過pack op完成pixel transfer。[OpenGL 3.3][05/04/2013]

8.Default Framebuffer: 只要glFramebuffer綁定到0上時,default framebuffer就啟用了,可以對其的image進行復制操作。Direct3D並沒有framebuffer的概念!通過 DXGI初始化Direct3D10的時候,已經創建了相應的back buffer,這個back buffer才能完成和OpenGL一致的工作。[OpenGL 3.3][04/24/2013]

9.Framebuffer Completeness: Depth Stencil Target的數據類型必須和綁定的類型一致,如果Depth Stencil Target的數據類型只有Depth分量而沒有Stencil分量,那么不可以把它綁定到GL_DEPTH_STENCIL_ATTACHMENT上去,只能綁定到GL_DEPTH_ATTACHMENT上去,否則會導致Framebuffer Incompleteness錯誤而導致不能使用;同樣的如果Depth Stencil Target有Depth分量也有Stencil分量,那么必須將其綁定到GL_DEPTH_STENCIL_ATTACHMENT上去。[OpenGL 3.3][07/01/2013]

 

Others

1.Vertical Synchronization: 在AMD上OpenGL默認是關閉垂直同步的,但是在Nvidia上同樣的程序確實默認開啟的,兩者的行為不一致;使用WGL的擴展wglSwapIntervalEXT來開啟和關閉垂直同步,關閉或者開啟幀率的限制,使其程序的行為在不同的設備上保持一致。[OpenGL WGL][10/02/2013]

2.Video Memory Check: 在有些情況下需要檢查剩余的顯存大小是否夠用,AMD的顯卡提供了GL_ATI_mem_info擴展,而Nvidia提供了GL_NVX_gpu_memory_info擴展,相應的擴展應該都被主流的顯卡驅動所支持了(我沒有非常多的硬件供我自己調查,但是我自己的HD 4300支持了GL_ATI_mem_info擴展,而GTX 650支持了GL_NVX_gpu_memory擴展)。[OpenGL][01/24/2014]

3.GPU Capability:Direct3D10開始沒有Max Capability,只有“Direct3D 10 Compatible”。

Unlike prior versions of the API, Direct3D 10 no longer uses "capability bits" (or "caps") to indicate which features are supported on a given graphics device. Instead, it defines a minimum standard of hardware capabilities which must be supported for a display system to be "Direct3D 10 compatible". This is a significant departure, with the goal of streamlining application code by removing capability-checking code and special cases based on the presence or absence of specific capabilities. Because Direct3D 10 hardware was comparatively rare after the initial release of Windows Vista and because of the massive installed base of non-Direct3D 10 compatible graphics cards, the first Direct3D 10-compatible games still provide Direct3D 9 render paths. Examples of such titles are games originally written for Direct3D 9 and ported to Direct3D 10 after their release, such as Company of Heroes, or games originally developed for Direct3D 9 with a Direct3D 10 path retrofitted later in development, such as Hellgate: London or Crysis.[OpenGL 3.3][04/30、2013]

 4.Shader Model: OpenGL中並沒有Shader Model的概念,取代之的是GL_SHADING_LANGUAGE_VERSION。從#version 330開始對應的是Shader Model 4.0;#version 430對應的是Shader Model 5.0。[OpenGL 3.3][05/05/2013]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM