My experience using OpenGL on the WASM platform
Solved
Qt for WebAssembly
-
I've noticed that many people are asking questions related to OpenGL. Based on my experience, I'd like to share some insights to help everyone get started more quickly.
- In WASM, you can normally develop 3D programs using OpenGL, with the same logic and process as when you develop on the Windows platform or the Android platform. And we only need to adapt to the WASM platform, whether your web is running on x86 Windows or ARM Android.
- On the WASM platform, we need to use WebGL, not OpenGL. They have similar syntax, but overall, WebGL has been trimmed down and is more streamlined. It's like how we use OpenGL/ES on iOS, which also has fewer features than OpenGL. Of course, for simplicity, you can refer to all these collectively as OpenGL, but be mindful to differentiate them when searching for resources.
- Although it's called WebGL, at the Qt programming level, we still use the QOpenGLXXX classes.
- Try to use QOpenGLFunctions instead of directly calling OpenGL functions, as this reduces a lot of library inclusion issues. For example, the glDrawArrays interface, a commonly used interface, has been wrapped by QOpenGLFunctions in Qt.
- When developing WebGL programs for WASM, you don't need to include any LIBS in the pro file or add header file paths. Qt has already taken care of library inclusion, and we simply need QT += opengl.
- We can use the same glsl file across all platforms. For WebGL and OpenGL/ES, you might need to add "precision highp float;" at the beginning, but the rest of the code can be the same.
- WebGL lacks an important feature: setting the line width (glLineWidth), which unfortunately, is not supported by browsers.
- If you're developing with QML, we can use the same OpenGL context as QML, allowing OpenGL to be drawn at the very bottom or top layer. Qt provides examples for this. You can also use FBO, which is QOpenGLFramebufferObject. Both methods allow you to use OpenGL in your QML programs.
Here are two small programs related to WASM/OpenGL that I've written for your reference:
- Rolling waves, modified based on a Qt example: https://web.jasonserver.com:10035/PBViewerDemo/PBViewerDemo.html
- Using FBO in QML: https://github.com/188080501/JQImageItem
-
J JasonWong has marked this topic as solved on
-
-
@shome
This is the GLSL code for this demo, which uses vertex shaders and fragment shaders.vert
// 输入变量 attribute vec4 rawVertex; attribute vec3 rawNormal; attribute vec3 rawTangent; attribute vec2 rawTexture; // 输出变量 varying vec4 currentVertex; varying vec3 currentNormal; varying vec3 currentTangent; varying vec2 currentTexture; varying float currentDepth; // 绘制参数 uniform mat4 paintMatrix; uniform sampler2D waveTexture; uniform float timeOffset; void main() { currentDepth = 1.0 - ( rawVertex.z / -0.6 ); vec4 wave = texture2D( waveTexture, rawTexture + vec2( timeOffset, -timeOffset ) * 0.0001 ); vec4 newVertex = rawVertex; const float amplitude = 0.05; const float frequency = 6.0; newVertex.x += ( wave.r * 0.05 ) * currentDepth; newVertex.y += ( wave.b * 0.05 ) * currentDepth; newVertex.z += amplitude * sin( frequency * ( newVertex.x - newVertex.y ) + timeOffset * 0.001 ) * currentDepth; currentVertex = paintMatrix * newVertex; currentNormal = normalize( mat3( paintMatrix[ 0 ].xyz, paintMatrix[ 1 ].xyz, paintMatrix[ 2 ].xyz ) * rawNormal ); currentTangent = normalize( mat3( paintMatrix[ 0 ].xyz, paintMatrix[ 1 ].xyz, paintMatrix[ 2 ].xyz ) * rawTangent ); currentTexture = rawTexture; gl_Position = currentVertex; gl_Position.z /= 5.0; // 防止被视景体裁剪 }
frag
// 输入参数 varying vec4 currentVertex; varying vec3 currentNormal; varying vec3 currentTangent; varying vec2 currentTexture; varying float currentDepth; // 绘制参数 uniform sampler2D normalTexture; uniform sampler2D foamTexture; uniform sampler2D specularTexture; uniform mat4 paintMatrix; vec3 processColor1(vec3 color, vec3 normal) { const vec3 ambientColor = vec3( 1.0, 1.0, 1.0 ); // 环境光参数 const vec3 lightPos = vec3( 0.0, 0.4, 1.4 ); // 光源位置 const vec3 viewPos = vec3( 0.0, 0.0, 1.0 ); // 观察位置 const vec3 lightColor = vec3( 0.5, 0.5, 0.5 ); // 光源颜色 const float shininess = 48.0; // 镜面反射强度 float specularStrength = texture2D( specularTexture, currentTexture ).x; // 镜面反射颜色系数 // 计算方向向量 vec3 lightDir = normalize( lightPos - viewPos ); vec3 viewDir = normalize( -viewPos ); // 环境光照 vec3 ambient = ambientColor * color; // 漫反射光照 float diff = max( dot( normal, lightDir ), 0.0 ); vec3 diffuse = diff * lightColor * color; // 镜面反射光照 vec3 reflectDir = reflect( -lightDir, normal ); float spec = pow( max( dot( viewDir, reflectDir ), 0.0 ), shininess ); vec3 specular = specularStrength * spec * lightColor; vec3 result = ambient + diffuse + ( ( spec <= 1.0 ) ? ( specular ) : ( vec3( 0.0, 0.0, 0.0 ) ) ); // 合并光照效果 return result; } void main() { const vec3 defaultColor = vec3( 0.15, 0.35, 0.50 ); if ( currentDepth < 0.999 ) { gl_FragColor = vec4( defaultColor * currentDepth * 1.5, 1.0 ); return; } vec4 foamColor = texture2D( foamTexture, currentTexture ); vec3 currentColor = defaultColor + foamColor.xyz / 3.5; vec3 normalFromTexture = texture2D( normalTexture, currentTexture ).xyz * 2.0 - 1.0; vec3 currentBitangent = cross( currentTangent, currentNormal ); mat3 TBN = mat3( currentTangent, currentBitangent, currentNormal ); vec3 normal = normalize( TBN * normalFromTexture ); gl_FragColor = vec4( processColor1( currentColor, normal ) * currentDepth, 1.0 ); }