Notas de lectura del código fuente de Three.js (cómo se organizan los objetos)_Conocimientos básicos
Este es el tercer artículo de las notas de lectura del código fuente de Three.js. Los dos artículos anteriores trataron principalmente sobre objetos centrales. Estos objetos centrales giran principalmente en torno a objetos vector vector3 y objetos matriz matriz4, centrándose en la posición y el cambio de un solo vértice en el espacio. Este artículo discutirá principalmente cómo se organizan los objetos en Three.js: es decir, cómo combinar vértices, superficies y materiales en un objeto específico.
Objeto::Mesh
Este constructor construye un objeto en el espacio. La razón por la que se llama "cuadrícula" es que, de hecho, los objetos con volumen se modelan básicamente como "cuadrículas".
El código es el siguiente:
THREE.Mesh = function (geometría, material) {
THREE.Object3D.call( this
this.geometry = geometría;
this.material = ( material !== indefinido ) ? material : new THREE.MeshBasicMaterial( { color : Math.random() * 0xffffff, wireframe : true } );
/* Algún otro contenido no relacionado con esta sección*/
}
De hecho, la clase Mesh tiene solo dos atributos, que representan el geometría de la forma geométrica Objeto y objeto material que representa el material. El objeto de geometría se introdujo en la publicación de blog anterior, y algunas clases derivadas se presentarán en esta publicación de blog (a través del proceso de construcción de estas clases derivadas, se puede comprender más claramente el principio de funcionamiento del objeto de malla y); sus clases derivadas también se cubrirán en esta nota. Estas dos propiedades del objeto Mesh están estrechamente relacionadas entre sí. En la matriz de caras del objeto de geometría, el índice de material de cada objeto de cara se usa para hacer coincidir el objeto de atributo de material, y la matriz vertexUVs del objeto de cara se usa para hacer coincidir. el valor de cada vértice de la matriz por turno. Vale la pena señalar que Mesh solo puede tener un objeto material (no sé cuál es el propósito de este diseño. Si es necesario utilizar varios materiales, los materiales deben inicializarse en el atributo de materiales de la geometría misma en orden). de materialIndex.
Geometry::CubeGeometry
Este constructor crea un objeto cubo.
El código es el siguiente:
TRES.CubeGeometry = función (ancho, alto, profundidad, segmentos de ancho, segmentos de altura, segmentos de profundidad) {
TRES.Geometry. llamar( esto );
var alcance = esto
this.width = ancho
this.height = altura
this.profundidad = profundidad;
var width_half = this.width / 2;
var height_half = this.height / 2; var profundidad_half = this. profundidad / 2 ;
/* Omitir*/
buildPlane('z', 'y', - 1, - 1, esta.profundidad, esta.altura, ancho_medio, 0 ); // px
/* Omitir*/
función buildPlane( u, v, udir, vdir, ancho, alto, profundidad, materialIndex ) {
/* Omitir*/
}
this.computeCentroids();
this.mergeVertices()
}; /p>
Lo más importante que hace el constructor es en buildPlane. Lo más importante de esta función es la operación del alcance (en el bloque de código anterior, el alcance es esto), que incluye: llamar a alcance.vertices.push(vector) para agregar vértices al objeto de geometría; llamar a alcance.faces.push(; face) para agregar La superficie se agrega al objeto de geometría y se llama al método alcance.faceVertexUvs[i].push(uv) para agregar las coordenadas del material correspondientes a los vértices al objeto de geometría. La mayor parte del código trata sobre la lógica de generar cubos, que es fácil de entender y fácil de extender a otros tipos de objetos geométricos.
Los parámetros del constructor incluyen largo, ancho, alto y el número de segmentos en tres direcciones. La llamada segmentación significa que si los tres parámetros, como widthSeqments, se establecen en 2, cada cara se representará como 2 × 2 = 4 caras y el cubo completo se compone de 24 superficies, como una cuadrícula.
El código es el siguiente:
función buildPlane( u, v, udir, vdir, ancho, alto, profundidad, materialIndex) {
var w, ix, iy,
gridX = alcance.widthSegments,
gridY = alcance.heightSegments,
width_half = ancho / 2,
altura_mitad = altura / 2,
desplazamiento = alcance.vertices.length
if ( ( u === 'x' amp; v === 'y' ) || ( u === 'y' amp; v === 'x' ) ) {w = 'z' }
else if ( ( u === 'x' amp; amp; v === 'z' ) || ( u === 'z' amp; v === 'x' ) ) {w = 'y'; gridY = alcance.profundidadSegments } else if ( ( u === 'z' amplificador; v === 'y' ) || ( u === 'y' amplificador; v === 'z' ) ) {w = 'x' ; gridX = alcance.profundidadSegments;} p>
var gridX1 = gridX 1,
gridY1 = gridY 1,
segment_width = ancho/gridX,
segment_height = alto / gridY,
normal = new THREE.Vector3();
normal[ w ] = profundidad gt 0 ? iy for ( ix = 0; ix var vector = new THREE.Vector3();
vector[ u ] = ( ix * ancho_segmento - ancho_medio ) * udir;
vector[ v ] = ( iy * altura_segmento - altura_mitad ) * vdir;
vector[ w ] = profundidad
alcance.vertices.push( vector );
}
for ( iy = 0; iy for ( ix = 0; ix var a = ix gridX1 * iy;
var b = ix gridX1 * ( iy 1 );
var c = ( ix 1 ) gridX1 * ( iy 1
var d = ( ix 1 ) gridX1 * iy ; >var cara = nuevo TRES.Cara4( un desplazamiento, b apagado
establecer, desplazamiento c, desplazamiento d); normal.clone(), normal.clone() );
cara.materialIndex = materialIndex
alcance.faces.push( cara
alcance; .faceVertexUvs[ 0 ].push( [
nuevo TRES.UV( ix / gridX, 1 - iy / gridY ),
nuevo TRES.UV( ix / gridX, 1 - ( iy 1 ) / gridY ),
nuevo TRES.UV( ( ix 1 ) / gridX, 1- ( iy 1 ) / gridY ),
nuevo TRES.UV( ( ( ix 1 ) / gridX, 1 - iy / gridY )
]
}
}
}
Excepto por el método clone() que tienen la mayoría de los objetos, CubeGeometry no tiene otros métodos, y lo mismo ocurre con otros objetos XXXGeometry. No hay forma de explicar que este objeto es responsable de organizar y almacenar datos, y cómo utilizar estos datos para generar escenas y animaciones tridimensionales se define en otro objeto.
Geometry::CylinderGeometry
Como sugiere el nombre, este constructor crea un objeto cilíndrico (o frustum).
El código es el siguiente:
THREE.CylinderGeometry = function (radiusTop, radioBottom, altura, radioSegmentos, alturaSegmentos, openEnded) {
/* Omitido* /
/* Omitido*/
p>
}
Con la base del constructor CubeGeometry, deberíamos poder implementar CilindroGeometry nosotros mismos. Solo es necesario prestar atención al significado de cada parámetro del constructor. radioTop y radioBottom representan el radio de la parte superior e inferior, y la altura representa la altura. radioSegments define en cuántas partes se debe dividir el círculo (cuanto mayor sea el número, más redondo será el cilindro), heightSegments define en cuántas partes se debe dividir la altura total y openEnded especifica si se generan las superficies superior e inferior.
Hay otros dos puntos que vale la pena señalar en el código fuente: el origen local del modelo es el punto medio del eje central, no el centro de gravedad o similares, lo que significa que la altura de la parte superior la superficie circular (valor del eje y) es altura/2, la superficie circular inferior es -altura/2, lo cual no es diferente para los cilindros, pero también hay una diferencia para conos truncados con diferentes radios superior e inferior, la superficie superior; y el suelo de este modelo utilizan superficies tipo face3, mientras que el lateral utiliza una superficie tipo face4.
Geometry::SphereGeometry
Este constructor crea una esfera.
El código es el siguiente:
THREE.SphereGeometry = function (radio, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ){
/* ligeramente*/
}
El significado de cada parámetro: radio especifica el radio, widthSegments representa el número de segmentos de "longitud" y heightSegments representa el número de segmentos de "latitud". Los últimos cuatro parámetros son opcionales y representan el valor inicial de longitud y latitud. Cualquiera que esté familiarizado con las coordenadas polares sabe que la letra griega φ (phi) se usa generalmente para representar el ángulo del círculo de latitud (longitud), y theta (theta) se usa para representar el ángulo del círculo de longitud (latitud). Los valores predeterminados de estos cuatro números son 0, 2π, 0, π respectivamente. Al cambiar sus valores, puedes crear una esfera incompleta (pero los bordes deben estar limpios).
En el código fuente, a excepción del área dentro del círculo polar del Polo Norte y la Antártida, que usa una superficie de tipo face3, otras partes usan una superficie de tipo face4. El origen local es el centro de la esfera.
Geometry::PlaneGeometry
Este constructor crea un plano.
El código es el siguiente:
THREE.PlaneGeometry = función (ancho, alto, anchoSegmentos, altoSegmentos){
/* Omitido*/
}
El significado de cada parámetro: ancho, alto, número de segmentos de ancho y número de segmentos de alto. Los lectores deben estar familiarizados con esta forma de construir una "cuadrícula".
Se obtuvo otra información del código fuente: el plano está construido en el plano x-y y el origen es el punto central del rectángulo.
Geometry::ExtrudeGeometry
Este objeto es ahora un método para construir formas geométricas generales, pero generalmente almacenamos los objetos modelados en un archivo en un formato determinado y pasamos el cargador. en, por lo que parece haber pocas oportunidades para utilizar esta función directamente. Además, esta función parece ser un producto semiacabado. Muchas configuraciones están acumuladas en el objeto de opciones y no la estudié detenidamente.
Material::Material
El objeto Material es el objeto prototipo para todos los demás tipos de Material.
El código es el siguiente:
THREE.Material = function () {
THREE.MaterialLibrary.push( this
this.id = TRES.MaterialIdCount;
this.name = '';
this.side = TRES.FrontSide
this.opacity = 1 ;
p>
this.transparent = false;
this.blending = TRES.NormalBlending
this.blendSrc = TRES.SrcAlphaFactor
this.blendEquation = THREE.OneMinusSrcAlphaFactor;
this.blendEquation = THREE.AddEquation
this. DepthTest = true; . DepthWrite = verdadero;
this.polygonOffset = false;
this.polygonOffsetFactor = 0
this.polygonOffsetUnits = 0; >this.alphaTest = 0;
this.overdraw = false; // Booleano para corregir lagunas de suavizado en CanvasRenderer
this.visible = true
this; .needsUpdate = true;
};
Veamos primero algunos atributos más importantes:
El atributo opacidad es un valor en el rango de 0-1. , indicando transparencia. El atributo transparente especifica si se usa la transparencia. Solo cuando el valor sea verdadero, se mezclará con él (transparente significa que al renderizar píxeles, el valor que se va a renderizar y el valor existente trabajan juntos para calcular el valor del píxel renderizado). lograr el efecto de mezcla).
Los atributos blending, blendSrc, blendDst y blendEquation especifican el método de combinación y el método de especificación de peso de la fuente de combinación Src y el valor de píxel existente Dst del píxel de combinación. De forma predeterminada (como el valor predeterminado asignado en el constructor), el nuevo valor de píxel es igual a: nuevo valor × alfa valor antiguo × (1-alfa).
No entendía por qué no había ningún objeto más importante en la clase Material, que representa las propiedades de la imagen de textura. Luego entendí que en realidad hay una diferencia entre materiales y texturas. Sólo se puede decir que ciertos materiales tienen texturas, pero también hay materiales que no tienen texturas. El material afecta el efecto de renderizado de toda la forma. Por ejemplo: "Renderiza una línea de 5 píxeles de ancho, con ambos extremos como cuadrados, rojo opaco". Esta descripción puede considerarse un material y no implica ninguna textura.
Como muchos objetos Geometry, el objeto Material no tiene otros métodos excepto los métodos generales clone(), dellocate() y setValues(). Los siguientes son los dos objetos materiales más básicos.
Material::LineBasicMaterial
Este constructor crea un material para representar formas lineales.
El código es el siguiente:
THREE.LineBasicMaterial = función (parámetros) {
THREE.Material.call( this
<); p>this .color = new THREE.Color( 0xffffff );this.linewidth = 1
this.linecap = 'ronda'; .linejoin = 'redondo';
this.vertexColors = false;
this.fog = true
this.setValues( parámetros
};
Como sugieren sus nombres, los atributos color y ancho de línea se refieren al color y ancho de la línea (la línea no tiene ancho, el ancho aquí se usa para renderizar).
Los atributos linecap y linejoin especifican el estilo de los puntos finales de línea y los puntos de conexión.
El atributo niebla especifica si este material se ve afectado por la niebla.
Material::MeshBasicMaterial
Este constructor crea el material utilizado para renderizar la superficie de malla.
El código es el siguiente:
THREE.MeshBasicMaterial = función (parámetros) {
THREE.Material.call( this
<); p>this .color = new THREE.Color( 0xffffff ); // emisivothis.map = null
this.lightMap = null; this.specularMap = null;
this.envMap = null;
this.combine = THREE.MultiplyOperation
this.reflectivity = 1; >
this.refractionRatio = 0.98;
this.fog = true;
this.shading = THREE.SmoothShading
this.wireframe = false;
p>
this.wireframeLinewidth = 1;
this.wireframeLinecap = 'redondo'
this.wireframeLinejoin = 'redondo'; /p>
this.vertexColors = TRES.NoColors;
this.skinning = false
this.morphTargets = false
this.setValues (parámetros);
};
Aquí aparecen los atributos de textura más importantes, incluidos map, lightMap y specularMap, que son todos objetos de tipo textura.
El atributo estructura alámbrica especifica si se representa la línea límite de la superficie. Si se representa, los siguientes atributos que comienzan con estructura alámbrica indican cómo se representará si se representa la línea límite.
Textura::Textura
Este constructor se utiliza para crear objetos de textura.
El código es el siguiente:
THREE.Texture = función (imagen, mapeo, wrapS, wrapT, magFilter, minFilter, formato, tipo, anisotropía) {
TRES .TextureLibrary.push( este );
este.id = TRES.TextureIdCount
este.nombre = ''; = imagen;
this.mapping = mapeo !== indefinido ? mapeo : new THREE.UVMapping();
this.wrapS = wrapS !== indefinido ? ClampToEdgeWrapping;
this.wrapT = wrapT !== indefinido ? p>
this.minFilter = minFilter!== indefinido? minFilter: THREE.LinearMipMapLinearFilter;
this.anisotropy = anisotropía!== anisotropía: 1; .format = formato!== indefinido? formato: THREE.RGBAFormat;
this.type = tipo!== indefinido tipo: THREE.UnsignedByteType; TRES.Vector2 ( 0, 0 );
this.repeat = nuevo TRES.Vector2( 1, 1 );
this.generateMipmaps = verdadero
this.premultiplyAlpha = falso;
this.flipY = verdadero;
this.needsUpdate = falso
this.onUpdate = nulo; p>} ;
El atributo más importante es la imagen, que es un objeto de tipo Imagen de JavaScript. El primer parámetro que se pasa es el objeto. Más adelante se describirá cómo crear el objeto.
Los siguientes objetos son todos opcionales. Si se omiten, se completará el valor predeterminado, que a menudo se completa.
Los atributos magFileter y minFileter especifican el método de filtrado de la textura al acercar y alejar: punto vecino más cercano, interpolación bilineal, etc.
Para generar una textura a partir de la URL, es necesario llamar a Three.ImageUtils.loadTexture(paras). Esta función devuelve un objeto de tipo textura. La función THREE.ImageLoader.load(paras) se llama dentro de la función, y el constructor THREE.Texture() se llama dentro de esta función para generar la textura.