1

Trying to implement refraction in OpenGL ES 2.0/3.0. Used the following shaders:

Vertex shader:

#version 300 es
precision lowp float; 
uniform mat4 u_mvMatrix;

in vec4 a_position;  
in vec3 a_normal;
...
out mediump vec2 v_refractCoord;

const mediump float eta = 0.95;

void main() {
    vec4 eyePositionModel = u_mvMatrix * a_position;

    // eye direction in model space
    mediump vec3 eyeDirectModel = normalize(a_position.xyz - eyePositionModel.xyz);

    // calculate refraction direction in model space
    mediump vec3 refractDirect = refract(eyeDirectModel, a_normal, eta);

    // project refraction
    refractDirect = (u_mvpMatrix * vec4(refractDirect, 0.0)).xyw;

    // map refraction direction to 2d coordinates
    v_refractCoord = 0.5 * (refractDirect.xy / refractDirect.z) + 0.5;
    ...
}

Fragment shader:

...
in mediump vec2 v_refractCoord;
uniform samplerCube s_texture; // skybox

void main() {
    outColor = texture(s_texture, vec3(v_refractCoord, 1.0));
}

Method for loading texture:

@JvmStatic
fun createTextureCubemap(context: Context, rowID: Int) {
    val input = context.resources.openRawResource(rowID)
    val bitmap = BitmapFactory.decodeStream(input)

    val textureId = IntArray(1)
    glGenTextures(1, textureId, 0)
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureId[0])

    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, bitmap, 0)

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    return textureId[0]
}

But the texture is obtained with large pixels like:

enter image description here

What could be the reason for this? Maybe this is normal for a low-poly model? It seems that the texture is too close.

Note: The fewer polygons - the less quality becomes.

Thanks in advance for any comment/answer!

image from goodfon.ru

Solution: On the @Rabbid76 advice, I changed the normal data. It turned out that in the Blender you need to set the Shading for the object as smooth (no flat) - this increases the number of normals when exporting to the format *.obj: Why OBJ export writes face normals instead of vertex normals

Also, on the @Rabbid76 advice, I changed the line:

vec3 eyeDirectModel = normalize(- eyePositionModel.xyz);

As a result, pixelation has disappeared:

enter image description here

In addition, pixel artifacts may also appear when calculate refraction in the vertex shader, so I transferred the calculations to the fragment shader. Here is the modified shader code:

Vertex shader:

#version 300 es
precision lowp float;
uniform mat4 u_mvpMatrix;
uniform mat4 u_mvMatrix;

in vec4 a_position; 
in vec3 a_normal;

out vec3 v_normal;
out lowp float SpecularIntensity;

out vec3 v_eyeDirectModel;

float getSpecularIntensity(vec4 position, vec3 a_normal, vec3 eyeDirectModel) {
    float shininess = 30.0;
    vec3 lightPosition = vec3(-20.0, 0.0, 0.0);
    mediump vec3 LightDirModel = normalize(lightPosition - position.xyz);
    mediump vec3 halfVector = normalize(LightDirModel + eyeDirectModel);
    lowp float NdotH = max(dot(a_normal, halfVector), 0.0);
    return pow(NdotH, shininess);
}

void main() {
    v_normal = a_normal;
    vec4 eyePositionModel = u_mvMatrix * a_position;
    // Eye direction in model space
    vec3 eyeDirectModel = normalize(- eyePositionModel.xyz);
    // specular lighting
    SpecularIntensity = getSpecularIntensity(a_position, a_normal, eyeDirectModel);
    v_eyeDirectModel = eyeDirectModel;
    gl_Position = u_mvpMatrix * a_position;
}

Fragment shader:

#version 300 es
precision lowp float;
uniform mat4 u_mvpMatrix;

in vec3 v_normal;
in lowp float SpecularIntensity;
in vec3 v_eyeDirectModel;

out vec4 outColor; 
uniform samplerCube s_texture; // skybox

const float eta = 0.65;
void main() {
    // Calculate refraction direction in model space
    vec3 refractDirect = refract(v_eyeDirectModel, normalize(v_normal), eta);
    // Project refraction
    refractDirect = (u_mvpMatrix * vec4(refractDirect, 0.0)).xyw;
    // Map refraction direction to 2d coordinates
    vec2 refractCoord = 0.5 * (refractDirect.xy / refractDirect.z) + 0.5;

    vec4 glassColor = texture(s_texture, vec3(refractCoord, 1.0));
    outColor = glassColor + SpecularIntensity;
    outColor.a = 0.8; // transparent
}
alexrnov
  • 2,346
  • 3
  • 18
  • 34
  • 1
    Set the texture minifying function parameter to `G_LLINEAR`. See [`glTexParameter`](https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glTexParameter.xhtml): `glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)`. – Rabbid76 May 08 '20 at 14:57
  • @Rabbid76 Thank You for attention to the problem! Did as you said and added the code in answer. But this has not helped yet. In general, it seems that the pixel size corresponds to the size of the polygon, maybe there is an error in the shader? – alexrnov May 08 '20 at 23:48
  • 1
    I've seen artifacts like that on some hardware when the integer part of the UV coordinate has got large (e.g. 100.3 instead of 0.3). I haven't worked through the maths in the vertex shader to figure out if that's likely to be the problem, but I'd start by trying to visualize the UVs to check if they're sensible. – Columbo May 09 '20 at 16:58
  • @Columbo Made UV-unwrapping in Blender - looks good without a refraction effect. Also transferred the calculations to the fragment shader, but the problem remained. But thanks for the advice! I will look in this direction. – alexrnov May 09 '20 at 23:37
  • @alexrnov Do you compute the normal vectors per vertex or do you use the face normal vectors? – Rabbid76 May 16 '20 at 11:35
  • @Rabbid76 Sorry, but I dont know))). Data of normals is taken from the *.obj file (from Blender) in format: vn 0.5416 -0.3089 0.7818. The shader code is partially taken from PVRShamanGUI. There the picture is good. – alexrnov May 16 '20 at 11:54

1 Answers1

1

First of all there is a mistake in the shader code. a_position.xyz - eyePositionModel.xyz does not make any sense, since a_position is the vertex coordinate in model space and eyePositionModel is the vertex coordinate in view space.
You have to compute the incident vector for refract in view sapce. That is the vector from the eye position to the vertex. Since the eye position in view space is (0, 0, 0), it is:

vec4 eyePositionView = u_mvMatrix * a_position;

// eye direction in model space
mediump vec3 eyeDirectView = normalize(- eyePositionView.xyz);

Furthermore, it is an issue of the normal vector attributes. The problem is caused by the fact that the normal vectors are computed per face rather than individually for each vertex.
Note, the refraction direction (refractDirect) depends on the vertex coordinate (eyeDirectModel) and the normal vector (a_normal):

mediump vec3 refractDirect = refract(eyeDirectModel, a_normal, eta);

Since the normal vectors are different between adjacent surfaces, you can see a noticeable edge between the faces of the the mesh.

If the normal vectors are computed per vertex, then the adjacent faces share the vertex coordinates and the corresponding normal vectors. That would causes a smooth transition from face to face.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 1
    Could you please tell me if it is necessary to change the data of normals or make changes to the shader code? – alexrnov May 16 '20 at 12:01
  • 1
    @alexrnov The shader is correct. As mentiond in the answer, the normal vectors apper to be face normal vectors. It is a matter of the normal vector attributes rather than the shader. – Rabbid76 May 16 '20 at 12:04
  • Thank You very much!!! I will change the data for the normal attribute. – alexrnov May 16 '20 at 12:15
  • Stunningly! Using even one: normalize(- eyePositionView.xyz) made the image much-much better now! – alexrnov May 16 '20 at 15:09