RawShaderMaterial and WebGL 2

I current study shaderMaterial and want begin with simple triangle. But I can’t figure out what’s going wrong.
here is my code. I copy from example: webgl_buffergeometry_rawshader and try to change it to webgl2 and only display a single triangle .
html

<head>
<title>webgl_buffergeometry_rawshader</title>
<link rel="stylesheet" href="/threejsExample/f01-triangle/main.css">
</head>

<body>
<div id="container"></div>
<script src="/lib/three.js"></script>
<script src="/lib/WebGL.js"></script>
<script src="/lib/stats.min.js"></script>

<script id="vs" type="x-shader/x-vertex">#version 300 es
in vec4 a_position;
void main() {
    gl_Position = a_position;
}
	</script>

<script id="fs" type="x-shader/x-fragment">#version 300 es
precision mediump float;
out vec4 outColor;
void main() {
    outColor = vec4(1, 0, 0, 1);
}
		</script>

<script src="/threejsExample/f01-triangle/main.js"></script>
</body>

</html>

css

body {
color: #ffffff;
font-family: Monospace;
font-size: 13px;
text-align: center;
font-weight: bold;
background-color: #ffffff;
margin: 0px;
overflow: hidden;
}

#container {
width: 400px;
height: 400px;
background-color: white;
}

js:

if (WEBGL.isWebGL2Available() === false) {
document.body.appendChild(WEBGL.getWebGL2ErrorMessage());
}
var container;
var camera, scene, renderer;
init();
render();
// animate();
function init() {
    container = document.getElementById('container');
    camera = new THREE.PerspectiveCamera(50, container.clientWidth / container.clientHeight, 1, 10);
    camera.position.z = 2;
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xffffff);
    // geometry
    var positions = [
        0, 0, 0,
        0, 1, 0,
        1, 0, 0
    ];

var geometry = new THREE.BufferGeometry();

var positionAttribute = new THREE.Float32BufferAttribute(positions, 3);

geometry.addAttribute('position', positionAttribute);

// material

var material = new THREE.RawShaderMaterial( {
    uniforms: {
    },
    vertexShader: document.getElementById( 'vs' ).textContent.trim(),
    fragmentShader: document.getElementById( 'fs' ).textContent.trim(),
    side: THREE.DoubleSide,
     // transparent: true
   
} );

var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);


var canvas = document.createElement('canvas');
var context = canvas.getContext('webgl2');
renderer = new THREE.WebGLRenderer({ canvas: canvas, context: context });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(container.clientWidth, container.clientHeight);
// renderer.setClearColor( 0xffffff, 0);
container.appendChild(renderer.domElement);

window.addEventListener('resize', onWindowResize, false);
}


function onWindowResize() {
    camera.aspect = container.clientWidth / container.clientHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(container.clientWidth, container.clientHeight);
}
//

function animate() {
    // requestAnimationFrame(animate);
    // render();
}
function render() {
    renderer.render(scene, camera);
}

Working demo: https://jsfiddle.net/f2Lommf5/16468/

Some feedback:

  • The winding order of your vertices was wrong.
  • If you want to use RawShaderMaterial you have to define projectionMatrix and modelViewMatrix in the vertex shader. You need them for the model-view-projection transform.
  • Your vertex position attribute is wrong. It has to be a vec3 and the name has to be position.
1 Like

thanks again @Mugen87, I finally did it . This is an important start that let me correspond Threejs and pure webgl. I want to see how threejs reduce our work.

2 things :
– remove projectionMatrix and modelViewMatrix , it still work.
– Is there any other restriction such as "the name should be position "

The following documentation about built-in uniforms and attributes might help:

https://threejs.org/docs/index.html#api/en/renderers/webgl/WebGLProgram

1 Like

great!! thanks alot

Hi, @Mugen87. problems comes again. I want go further to render a rectangle with a texture using the same approach
so I add

 let rect = {};
  rect.posData = [
    0, 0, 0,
    0, 1, 0,
    1, 1, 0,
    1, 1, 0,
    1, 0, 0,
    0, 0, 0,
  ];
  rect.texData2 = [
    -1, -1,
    -1, 2,
    2, 2,
    2, 2,
    2, -1,
    -1, -1,
  ];

and then add

  var uvAttribute = new THREE.Float32BufferAttribute(rect.texData2, 2);
  geometry.addAttribute('uv', uvAttribute);

I load a texture

var texture = new THREE.TextureLoader().load('/assets/img/favicon.png');
and I pass a uniform

  var material = new THREE.RawShaderMaterial({
    uniforms: {
      texture1: { type: "t", value: texture }
    },
    vertexShader: res["first.vert"],
    fragmentShader: res["first.frag"],
    side: THREE.DoubleSide,
    // transparent: true

  });

finally I change the shader
vert:

#version 300 es

in vec3 position;

in vec2 uv;

out vec2 v_uv;

void main() {

gl_Position = vec4(position, 1);

v_uv = uv;

}

frag:

#version 300 es
precision mediump float;
uniform sampler2D texture1;
in vec2 v_uv;
out vec4 outColor;
void main() {
  outColor = texture(texture1, v_uv);
//   outColor = vec4(1, 0, 0, 1);
}

It don’t work ! Help!!
BTW, if you disable the comment in the last line , It did render a red rectangle

Working example: https://jsfiddle.net/f2Lommf5/16541/

You uv coordinates should be normally in the range [0,1]. Besides, the winding order of your triangles is incorrect again. You have to use CCW not CW. This error is not obvious since you set the side property to THREE.DoubleSide.

1 Like

I find the real reason . Using your code as reference I can quickly figure out the real mistake of my own , sorry I don’t post whole code .
The problem is that I only render 1 frame while you use a animation loop. So when rendering , my texture have not loaded yet . aHa ! Now I can go myself for a while. Thank you for your support!